diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index bffb88ebb7..6d8889f98d 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -1,9 +1,9 @@ --- name: "\U0001F41E Bug report" about: Create a report to help us improve -title: "[Bug] " -labels: bug -assignees: ashvayka, vvlladd28 +title: "Your title here" +labels: ['bug', 'unconfirmed'] +assignees: AndriichnekoDm --- @@ -12,11 +12,13 @@ A clear and concise description of what the bug is. **Your Server Environment** -* demo.thingsboard.io -* cloud.thingsboard.io +* [https://demo.thingsboard.io](demo.thingsboard.io) +* [https://thingsboard.cloud](thingsboard.cloud) * own setup - * cloud or local infrastructure or docker deployment + * Deployment: monolith or microservices + * Deployment type: deb, rpm, exe, docker-compose, k8s, ami * ThingsBoard Version + * Community or Professional Edition * OS Name and Version **Your Client Environment** @@ -54,7 +56,7 @@ Steps to reproduce the behavior: A clear and concise description of what you expected to happen. **Screenshots** -If applicable, add screenshots to help explain your problem. +If applicable, please add screenshots to help explain your problem. **Additional context** -Add any other context about the problem here. +Please feel free to add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 8730336bba..c73f810cd8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,9 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: "[Feature Request]" -labels: feature -assignees: ikulikov +title: "Your title here" +labels: ['feature'] +assignees: 'AndriichnekoDm' --- diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 99d82ca698..2f15690f68 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,9 +1,9 @@ --- name: Question -about: Describe your questions in details -title: "[Question] your title here" -labels: question -assignees: ashvayka +about: Describe your questions in detail +title: "Your title here" +labels: ['question'] +assignees: 'AndriichnekoDm' --- @@ -16,7 +16,7 @@ assignees: ashvayka * Generic **Description** -A clear and concise details. +Clear and concise details. **Environment** diff --git a/.github/workflows/license-header-format.yml b/.github/workflows/license-header-format.yml new file mode 100644 index 0000000000..20b5e7f063 --- /dev/null +++ b/.github/workflows/license-header-format.yml @@ -0,0 +1,54 @@ +# +# Copyright © 2016-2024 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. +# + +name: License header format + +on: + push: + branches: + - 'master' + - 'develop/3*' + - 'hotfix/3*' + +jobs: + license-format: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'corretto' # https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions + java-version: '21' + cache: 'maven' # https://github.com/actions/setup-java?tab=readme-ov-file#caching-sbt-dependencies + + - name: License header format + run: mvn -T 1C license:format + + - name: License header format (msa/black-box-tests/) + run: mvn -T 1C license:format -f msa/black-box-tests/ + + - name: Set Git user information + run: | + git config user.name "ThingsBoard Bot" + git config user.email "noreply@thingsboard.io" + + - name: Check and push changes + run: | + git diff --exit-code || git commit -am "License header format" && git push diff --git a/application/src/main/data/json/system/widget_bundles/buttons.json b/application/src/main/data/json/system/widget_bundles/buttons.json new file mode 100644 index 0000000000..effff6a27a --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -0,0 +1,15 @@ +{ + "widgetsBundle": { + "alias": "buttons", + "title": "Buttons", + "image": "tb-image:YnV0dG9ucy5zdmc=:IkJ1dHRvbnMiIHN5c3RlbSBidW5kbGUgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NjMgNTEuMzMzM1Y0NC4zMzMzSDY2LjgzM1Y1MS4zMzMzSDcyLjY2NjNWNDJINzYuMTY2M0w2NC40OTk3IDMxLjVMNTIuODMzIDQySDU2LjMzM1Y1MS4zMzMzSDYyLjE2NjNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDQyLjkzMTZIODYuMjFMODYuMTg4NSA0MC45NjU4SDg5LjY2ODlDOTAuMjU2MiA0MC45NjU4IDkwLjc1MzkgNDAuODc5OSA5MS4xNjIxIDQwLjcwOEM5MS41Nzc1IDQwLjUyOSA5MS44OTI2IDQwLjI3NDcgOTIuMTA3NCAzOS45NDUzQzkyLjMyMjMgMzkuNjA4NyA5Mi40Mjk3IDM5LjIwNDEgOTIuNDI5NyAzOC43MzE0QzkyLjQyOTcgMzguMjA4NyA5Mi4zMjk0IDM3Ljc4MjYgOTIuMTI4OSAzNy40NTMxQzkxLjkyODQgMzcuMTIzNyA5MS42MjA0IDM2Ljg4MzggOTEuMjA1MSAzNi43MzM0QzkwLjc5NjkgMzYuNTgzIDkwLjI3NDEgMzYuNTA3OCA4OS42MzY3IDM2LjUwNzhIODcuMDI2NFY1MEg4NC4zMzAxVjM0LjM1OTRIODkuNjM2N0M5MC40OTYxIDM0LjM1OTQgOTEuMjYyNCAzNC40NDE3IDkxLjkzNTUgMzQuNjA2NEM5Mi42MTU5IDM0Ljc3MTIgOTMuMTkyNCAzNS4wMjkgOTMuNjY1IDM1LjM3OTlDOTQuMTQ0OSAzNS43MjM2IDk0LjUwNjUgMzYuMTYwNSA5NC43NSAzNi42OTA0Qzk1LjAwMDcgMzcuMjIwNCA5NS4xMjYgMzcuODUwNiA5NS4xMjYgMzguNTgxMUM5NS4xMjYgMzkuMjI1NiA5NC45NzIgMzkuODE2NCA5NC42NjQxIDQwLjM1MzVDOTQuMzU2MSA0MC44ODM1IDkzLjkwMTQgNDEuMzE2NyA5My4yOTk4IDQxLjY1MzNDOTIuNjk4MiA0MS45ODk5IDkxLjk0OTkgNDIuMTkwNCA5MS4wNTQ3IDQyLjI1NDlMOTAuMTk1MyA0Mi45MzE2Wk05MC4wNzcxIDUwSDg1LjM2MTNMODYuNTc1MiA0Ny44NjIzSDkwLjA3NzFDOTAuNjg1OSA0Ny44NjIzIDkxLjE5NDMgNDcuNzYyIDkxLjYwMjUgNDcuNTYxNUM5Mi4wMTA3IDQ3LjM1MzggOTIuMzE1MSA0Ny4wNzEgOTIuNTE1NiA0Ni43MTI5QzkyLjcyMzMgNDYuMzQ3NyA5Mi44MjcxIDQ1LjkyMTUgOTIuODI3MSA0NS40MzQ2QzkyLjgyNzEgNDQuOTI2MSA5Mi43Mzc2IDQ0LjQ4NTcgOTIuNTU4NiA0NC4xMTMzQzkyLjM3OTYgNDMuNzMzNyA5Mi4wOTY3IDQzLjQ0MzcgOTEuNzEgNDMuMjQzMkM5MS4zMjMyIDQzLjAzNTUgOTAuODE4NCA0Mi45MzE2IDkwLjE5NTMgNDIuOTMxNkg4Ny4xNjZMODcuMTg3NSA0MC45NjU4SDkxLjEyOTlMOTEuNzQyMiA0MS43MDdDOTIuNjAxNiA0MS43MzU3IDkzLjMwNyA0MS45MjU1IDkzLjg1ODQgNDIuMjc2NEM5NC40MTcgNDIuNjI3MyA5NC44MzI0IDQzLjA4MiA5NS4xMDQ1IDQzLjY0MDZDOTUuMzc2NiA0NC4xOTkyIDk1LjUxMjcgNDQuODAwOCA5NS41MTI3IDQ1LjQ0NTNDOTUuNTEyNyA0Ni40NDA4IDk1LjI5NDMgNDcuMjc1MSA5NC44NTc0IDQ3Ljk0ODJDOTQuNDI3NyA0OC42MjE0IDkzLjgwODMgNDkuMTMzNSA5Mi45OTkgNDkuNDg0NEM5Mi4xODk4IDQ5LjgyODEgOTEuMjE1OCA1MCA5MC4wNzcxIDUwWk0xMDUuMjE2IDQ3LjI2MDdWMzguMzc3SDEwNy44MTVWNTBIMTA1LjM2NkwxMDUuMjE2IDQ3LjI2MDdaTTEwNS41ODEgNDQuODQzOEwxMDYuNDUxIDQ0LjgyMjNDMTA2LjQ1MSA0NS42MDI5IDEwNi4zNjUgNDYuMzIyNiAxMDYuMTkzIDQ2Ljk4MTRDMTA2LjAyMSA0Ny42MzMxIDEwNS43NTcgNDguMjAyNSAxMDUuMzk4IDQ4LjY4OTVDMTA1LjA0IDQ5LjE2OTMgMTA0LjU4MiA0OS41NDUyIDEwNC4wMjMgNDkuODE3NEMxMDMuNDY1IDUwLjA4MjQgMTAyLjc5NSA1MC4yMTQ4IDEwMi4wMTUgNTAuMjE0OEMxMDEuNDQ5IDUwLjIxNDggMTAwLjkzIDUwLjEzMjUgMTAwLjQ1NyA0OS45Njc4Qzk5Ljk4NDQgNDkuODAzMSA5OS41NzYyIDQ5LjU0ODggOTkuMjMyNCA0OS4yMDUxQzk4Ljg5NTggNDguODYxMyA5OC42MzQ0IDQ4LjQxMzcgOTguNDQ4MiA0Ny44NjIzQzk4LjI2MiA0Ny4zMTA5IDk4LjE2ODkgNDYuNjUyIDk4LjE2ODkgNDUuODg1N1YzOC4zNzdIMTAwLjc1OFY0NS45MDcyQzEwMC43NTggNDYuMzI5OCAxMDAuODA4IDQ2LjY4NDIgMTAwLjkwOCA0Ni45NzA3QzEwMS4wMDggNDcuMjUgMTAxLjE0NSA0Ny40NzU2IDEwMS4zMTYgNDcuNjQ3NUMxMDEuNDg4IDQ3LjgxOTMgMTAxLjY4OSA0Ny45NDExIDEwMS45MTggNDguMDEyN0MxMDIuMTQ3IDQ4LjA4NDMgMTAyLjM5MSA0OC4xMjAxIDEwMi42NDggNDguMTIwMUMxMDMuMzg2IDQ4LjEyMDEgMTAzLjk2NiA0Ny45NzY5IDEwNC4zODkgNDcuNjkwNEMxMDQuODE4IDQ3LjM5NjggMTA1LjEyMyA0Ny4wMDI5IDEwNS4zMDIgNDYuNTA4OEMxMDUuNDg4IDQ2LjAxNDYgMTA1LjU4MSA0NS40NTk2IDEwNS41ODEgNDQuODQzOFpNMTE2LjA0NyAzOC4zNzdWNDAuMjY3NkgxMDkuNDk0VjM4LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDM1LjUzMDNIMTEzLjk3NFY0Ni43ODgxQzExMy45NzQgNDcuMTQ2MiAxMTQuMDI0IDQ3LjQyMTkgMTE0LjEyNCA0Ny42MTUyQzExNC4yMzEgNDcuODAxNCAxMTQuMzc4IDQ3LjkyNjggMTE0LjU2NCA0Ny45OTEyQzExNC43NTEgNDguMDU1NyAxMTQuOTY5IDQ4LjA4NzkgMTE1LjIyIDQ4LjA4NzlDMTE1LjM5OSA0OC4wODc5IDExNS41NzEgNDguMDc3MSAxMTUuNzM1IDQ4LjA1NTdDMTE1LjkgNDguMDM0MiAxMTYuMDMzIDQ4LjAxMjcgMTE2LjEzMyA0Ny45OTEyTDExNi4xNDQgNDkuOTY3OEMxMTUuOTI5IDUwLjAzMjIgMTE1LjY3OCA1MC4wODk1IDExNS4zOTIgNTAuMTM5NkMxMTUuMTEyIDUwLjE4OTggMTE0Ljc5IDUwLjIxNDggMTE0LjQyNSA1MC4yMTQ4QzExMy44MyA1MC4yMTQ4IDExMy4zMDQgNTAuMTExIDExMi44NDYgNDkuOTAzM0MxMTIuMzg3IDQ5LjY4ODUgMTEyLjAyOSA0OS4zNDExIDExMS43NzEgNDguODYxM0MxMTEuNTE0IDQ4LjM4MTUgMTExLjM4NSA0Ny43NDQxIDExMS4zODUgNDYuOTQ5MlYzNS41MzAzWk0xMjMuNjIzIDM4LjM3N1Y0MC4yNjc2SDExNy4wN1YzOC4zNzdIMTIzLjYyM1pNMTE4Ljk2MSAzNS41MzAzSDEyMS41NVY0Ni43ODgxQzEyMS41NSA0Ny4xNDYyIDEyMS42IDQ3LjQyMTkgMTIxLjcgNDcuNjE1MkMxMjEuODA4IDQ3LjgwMTQgMTIxLjk1NCA0Ny45MjY4IDEyMi4xNDEgNDcuOTkxMkMxMjIuMzI3IDQ4LjA1NTcgMTIyLjU0NSA0OC4wODc5IDEyMi43OTYgNDguMDg3OUMxMjIuOTc1IDQ4LjA4NzkgMTIzLjE0NyA0OC4wNzcxIDEyMy4zMTIgNDguMDU1N0MxMjMuNDc2IDQ4LjAzNDIgMTIzLjYwOSA0OC4wMTI3IDEyMy43MDkgNDcuOTkxMkwxMjMuNzIgNDkuOTY3OEMxMjMuNTA1IDUwLjAzMjIgMTIzLjI1NCA1MC4wODk1IDEyMi45NjggNTAuMTM5NkMxMjIuNjg4IDUwLjE4OTggMTIyLjM2NiA1MC4yMTQ4IDEyMi4wMDEgNTAuMjE0OEMxMjEuNDA3IDUwLjIxNDggMTIwLjg4IDUwLjExMSAxMjAuNDIyIDQ5LjkwMzNDMTE5Ljk2NCA0OS42ODg1IDExOS42MDUgNDkuMzQxMSAxMTkuMzQ4IDQ4Ljg2MTNDMTE5LjA5IDQ4LjM4MTUgMTE4Ljk2MSA0Ny43NDQxIDExOC45NjEgNDYuOTQ5MlYzNS41MzAzWk0xMjUuMTE5IDQ0LjMxNzRWNDQuMDcwM0MxMjUuMTE5IDQzLjIzMjQgMTI1LjI0MSA0Mi40NTU0IDEyNS40ODQgNDEuNzM5M0MxMjUuNzI4IDQxLjAxNiAxMjYuMDc5IDQwLjM4OTMgMTI2LjUzNyAzOS44NTk0QzEyNy4wMDMgMzkuMzIyMyAxMjcuNTY4IDM4LjkwNjkgMTI4LjIzNCAzOC42MTMzQzEyOC45MDggMzguMzEyNSAxMjkuNjY3IDM4LjE2MjEgMTMwLjUxMiAzOC4xNjIxQzEzMS4zNjQgMzguMTYyMSAxMzIuMTIzIDM4LjMxMjUgMTMyLjc4OSAzOC42MTMzQzEzMy40NjIgMzguOTA2OSAxMzQuMDMyIDM5LjMyMjMgMTM0LjQ5NyAzOS44NTk0QzEzNC45NjMgNDAuMzg5MyAxMzUuMzE3IDQxLjAxNiAxMzUuNTYxIDQxLjczOTNDMTM1LjgwNCA0Mi40NTU0IDEzNS45MjYgNDMuMjMyNCAxMzUuOTI2IDQ0LjA3MDNWNDQuMzE3NEMxMzUuOTI2IDQ1LjE1NTMgMTM1LjgwNCA0NS45MzIzIDEzNS41NjEgNDYuNjQ4NEMxMzUuMzE3IDQ3LjM2NDYgMTM0Ljk2MyA0Ny45OTEyIDEzNC40OTcgNDguNTI4M0MxMzQuMDMyIDQ5LjA1ODMgMTMzLjQ2NiA0OS40NzM2IDEzMi44IDQ5Ljc3NDRDMTMyLjEzNCA1MC4wNjggMTMxLjM3OCA1MC4yMTQ4IDEzMC41MzMgNTAuMjE0OEMxMjkuNjgxIDUwLjIxNDggMTI4LjkxOCA1MC4wNjggMTI4LjI0NSA0OS43NzQ0QzEyNy41NzkgNDkuNDczNiAxMjcuMDEzIDQ5LjA1ODMgMTI2LjU0OCA0OC41MjgzQzEyNi4wODIgNDcuOTkxMiAxMjUuNzI4IDQ3LjM2NDYgMTI1LjQ4NCA0Ni42NDg0QzEyNS4yNDEgNDUuOTMyMyAxMjUuMTE5IDQ1LjE1NTMgMTI1LjExOSA0NC4zMTc0Wk0xMjcuNzA4IDQ0LjA3MDNWNDQuMzE3NEMxMjcuNzA4IDQ0Ljg0MDIgMTI3Ljc2MiA0NS4zMzQzIDEyNy44NjkgNDUuNzk5OEMxMjcuOTc3IDQ2LjI2NTMgMTI4LjE0NSA0Ni42NzM1IDEyOC4zNzQgNDcuMDI0NEMxMjguNjAzIDQ3LjM3NTMgMTI4Ljg5NyA0Ny42NTEgMTI5LjI1NSA0Ny44NTE2QzEyOS42MTMgNDguMDUyMSAxMzAuMDM5IDQ4LjE1MjMgMTMwLjUzMyA0OC4xNTIzQzEzMS4wMTMgNDguMTUyMyAxMzEuNDI4IDQ4LjA1MjEgMTMxLjc3OSA0Ny44NTE2QzEzMi4xMzcgNDcuNjUxIDEzMi40MzEgNDcuMzc1MyAxMzIuNjYgNDcuMDI0NEMxMzIuODg5IDQ2LjY3MzUgMTMzLjA1OCA0Ni4yNjUzIDEzMy4xNjUgNDUuNzk5OEMxMzMuMjggNDUuMzM0MyAxMzMuMzM3IDQ0Ljg0MDIgMTMzLjMzNyA0NC4zMTc0VjQ0LjA3MDNDMTMzLjMzNyA0My41NTQ3IDEzMy4yOCA0My4wNjc3IDEzMy4xNjUgNDIuNjA5NEMxMzMuMDU4IDQyLjE0MzkgMTMyLjg4NiA0MS43MzIxIDEzMi42NDkgNDEuMzc0QzEzMi40MiA0MS4wMTYgMTMyLjEyNyA0MC43MzY3IDEzMS43NjkgNDAuNTM2MUMxMzEuNDE4IDQwLjMyODUgMTMwLjk5OSA0MC4yMjQ2IDEzMC41MTIgNDAuMjI0NkMxMzAuMDI1IDQwLjIyNDYgMTI5LjYwMiA0MC4zMjg1IDEyOS4yNDQgNDAuNTM2MUMxMjguODkzIDQwLjczNjcgMTI4LjYwMyA0MS4wMTYgMTI4LjM3NCA0MS4zNzRDMTI4LjE0NSA0MS43MzIxIDEyNy45NzcgNDIuMTQzOSAxMjcuODY5IDQyLjYwOTRDMTI3Ljc2MiA0My4wNjc3IDEyNy43MDggNDMuNTU0NyAxMjcuNzA4IDQ0LjA3MDNaTTE0MC45MTMgNDAuODU4NFY1MEgxMzguMzI0VjM4LjM3N0gxNDAuNzYzTDE0MC45MTMgNDAuODU4NFpNMTQwLjQ1MSA0My43NTg4TDEzOS42MTMgNDMuNzQ4QzEzOS42MiA0Mi45MjQ1IDEzOS43MzUgNDIuMTY4OSAxMzkuOTU3IDQxLjQ4MTRDMTQwLjE4NiA0MC43OTM5IDE0MC41MDEgNDAuMjAzMSAxNDAuOTAyIDM5LjcwOUMxNDEuMzExIDM5LjIxNDggMTQxLjc5OCAzOC44MzUzIDE0Mi4zNjMgMzguNTcwM0MxNDIuOTI5IDM4LjI5ODIgMTQzLjU1OSAzOC4xNjIxIDE0NC4yNTQgMzguMTYyMUMxNDQuODEyIDM4LjE2MjEgMTQ1LjMxNyAzOC4yNDA5IDE0NS43NjkgMzguMzk4NEMxNDYuMjI3IDM4LjU0ODggMTQ2LjYxNyAzOC43OTU5IDE0Ni45MzkgMzkuMTM5NkMxNDcuMjY5IDM5LjQ4MzQgMTQ3LjUyIDM5LjkzMSAxNDcuNjkxIDQwLjQ4MjRDMTQ3Ljg2MyA0MS4wMjY3IDE0Ny45NDkgNDEuNjk2MyAxNDcuOTQ5IDQyLjQ5MTJWNTBIMTQ1LjM1VjQyLjQ4MDVDMTQ1LjM1IDQxLjkyMTkgMTQ1LjI2NyA0MS40ODE0IDE0NS4xMDMgNDEuMTU5MkMxNDQuOTQ1IDQwLjgyOTggMTQ0LjcxMiA0MC41OTcgMTQ0LjQwNCA0MC40NjA5QzE0NC4xMDQgNDAuMzE3NyAxNDMuNzI4IDQwLjI0NjEgMTQzLjI3NiA0MC4yNDYxQzE0Mi44MzIgNDAuMjQ2MSAxNDIuNDM1IDQwLjMzOTIgMTQyLjA4NCA0MC41MjU0QzE0MS43MzMgNDAuNzExNiAxNDEuNDM2IDQwLjk2NTggMTQxLjE5MiA0MS4yODgxQzE0MC45NTYgNDEuNjEwNCAxNDAuNzczIDQxLjk4MjcgMTQwLjY0NSA0Mi40MDUzQzE0MC41MTYgNDIuODI3OCAxNDAuNDUxIDQzLjI3OSAxNDAuNDUxIDQzLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgMTExVjExMy4zMzNINzUuNTIxM0w2NC4zMzMgMTI0LjUyMkw2NS45NzggMTI2LjE2N0w3Ny4xNjYzIDExNC45NzhWMTI1SDc5LjQ5OTdWMTExSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDEyMS45NzJDMTAwLjU2NCAxMjEuNjQ5IDEwMC41MTQgMTIxLjM2MyAxMDAuNDE0IDEyMS4xMTJDMTAwLjMyMSAxMjAuODYyIDEwMC4xNTMgMTIwLjYzMiA5OS45MDkyIDEyMC40MjVDOTkuNjY1NyAxMjAuMjE3IDk5LjMyMTkgMTIwLjAxNyA5OC44Nzc5IDExOS44MjNDOTguNDQxMSAxMTkuNjIzIDk3Ljg4MjUgMTE5LjQxOSA5Ny4yMDIxIDExOS4yMTFDOTYuNDU3NCAxMTguOTgyIDk1Ljc2OTkgMTE4LjcyOCA5NS4xMzk2IDExOC40NDhDOTQuNTE2NiAxMTguMTYyIDkzLjk3MjMgMTE3LjgzMiA5My41MDY4IDExNy40NkM5My4wNDEzIDExNy4wOCA5Mi42Nzk3IDExNi42NDcgOTIuNDIxOSAxMTYuMTZDOTIuMTY0MSAxMTUuNjY2IDkyLjAzNTIgMTE1LjA5NyA5Mi4wMzUyIDExNC40NTJDOTIuMDM1MiAxMTMuODE1IDkyLjE2NzYgMTEzLjIzNSA5Mi40MzI2IDExMi43MTJDOTIuNzA0OCAxMTIuMTg5IDkzLjA4NzkgMTExLjczOCA5My41ODIgMTExLjM1OEM5NC4wODMzIDExMC45NzIgOTQuNjc0MiAxMTAuNjc0IDk1LjM1NDUgMTEwLjQ2N0M5Ni4wMzQ4IDExMC4yNTIgOTYuNzg2OCAxMTAuMTQ1IDk3LjYxMDQgMTEwLjE0NUM5OC43NzA1IDExMC4xNDUgOTkuNzY5NSAxMTAuMzU5IDEwMC42MDcgMTEwLjc4OUMxMDEuNDUyIDExMS4yMTkgMTAyLjEwMSAxMTEuNzk1IDEwMi41NTIgMTEyLjUxOUMxMDMuMDEgMTEzLjI0MiAxMDMuMjM5IDExNC4wNCAxMDMuMjM5IDExNC45MTRIMTAwLjU2NEMxMDAuNTY0IDExNC4zOTggMTAwLjQ1MyAxMTMuOTQ0IDEwMC4yMzEgMTEzLjU1QzEwMC4wMTcgMTEzLjE0OSA5OS42ODcyIDExMi44MzQgOTkuMjQzMiAxMTIuNjA0Qzk4LjgwNjMgMTEyLjM3NSA5OC4yNTEzIDExMi4yNjEgOTcuNTc4MSAxMTIuMjYxQzk2Ljk0MDggMTEyLjI2MSA5Ni40MTA4IDExMi4zNTcgOTUuOTg4MyAxMTIuNTUxQzk1LjU2NTggMTEyLjc0NCA5NS4yNTA3IDExMy4wMDYgOTUuMDQzIDExMy4zMzVDOTQuODM1MyAxMTMuNjY0IDk0LjczMTQgMTE0LjAzNyA5NC43MzE0IDExNC40NTJDOTQuNzMxNCAxMTQuNzQ2IDk0Ljc5OTUgMTE1LjAxNCA5NC45MzU1IDExNS4yNThDOTUuMDcxNiAxMTUuNDk0IDk1LjI3OTMgMTE1LjcxNiA5NS41NTg2IDExNS45MjRDOTUuODM3OSAxMTYuMTI0IDk2LjE4ODggMTE2LjMxNCA5Ni42MTEzIDExNi40OTNDOTcuMDMzOSAxMTYuNjcyIDk3LjUzMTYgMTE2Ljg0NCA5OC4xMDQ1IDExNy4wMDlDOTguOTcxIDExNy4yNjcgOTkuNzI2NiAxMTcuNTUzIDEwMC4zNzEgMTE3Ljg2OEMxMDEuMDE2IDExOC4xNzYgMTAxLjU1MyAxMTguNTI3IDEwMS45ODIgMTE4LjkyMUMxMDIuNDEyIDExOS4zMTUgMTAyLjczNCAxMTkuNzYyIDEwMi45NDkgMTIwLjI2NEMxMDMuMTY0IDEyMC43NTggMTAzLjI3MSAxMjEuMzIgMTAzLjI3MSAxMjEuOTVDMTAzLjI3MSAxMjIuNjA5IDEwMy4xMzkgMTIzLjIwMyAxMDIuODc0IDEyMy43MzNDMTAyLjYwOSAxMjQuMjU2IDEwMi4yMjkgMTI0LjcwNCAxMDEuNzM1IDEyNS4wNzZDMTAxLjI0OCAxMjUuNDQxIDEwMC42NjEgMTI1LjcyNCA5OS45NzM2IDEyNS45MjVDOTkuMjkzMyAxMjYuMTE4IDk4LjUzNDIgMTI2LjIxNSA5Ny42OTYzIDEyNi4yMTVDOTYuOTQ0MyAxMjYuMjE1IDk2LjIwMzEgMTI2LjExNSA5NS40NzI3IDEyNS45MTRDOTQuNzQ5MyAxMjUuNzE0IDk0LjA5MDUgMTI1LjQwOSA5My40OTYxIDEyNS4wMDFDOTIuOTAxNyAxMjQuNTg2IDkyLjQyOSAxMjQuMDcgOTIuMDc4MSAxMjMuNDU0QzkxLjcyNzIgMTIyLjgzMSA5MS41NTE4IDEyMi4xMDQgOTEuNTUxOCAxMjEuMjczSDk0LjI0OEM5NC4yNDggMTIxLjc4MiA5NC4zMzQgMTIyLjIxNSA5NC41MDU5IDEyMi41NzNDOTQuNjg0OSAxMjIuOTMxIDk0LjkzMiAxMjMuMjI1IDk1LjI0NzEgMTIzLjQ1NEM5NS41NjIyIDEyMy42NzYgOTUuOTI3NCAxMjMuODQxIDk2LjM0MjggMTIzLjk0OEM5Ni43NjUzIDEyNC4wNTYgOTcuMjE2NSAxMjQuMTA5IDk3LjY5NjMgMTI0LjEwOUM5OC4zMjY1IDEyNC4xMDkgOTguODUyOSAxMjQuMDIgOTkuMjc1NCAxMjMuODQxQzk5LjcwNTEgMTIzLjY2MiAxMDAuMDI3IDEyMy40MTEgMTAwLjI0MiAxMjMuMDg5QzEwMC40NTcgMTIyLjc2NyAxMDAuNTY0IDEyMi4zOTQgMTAwLjU2NCAxMjEuOTcyWk0xMTAuNzcyIDEyNi4yMTVDMTA5LjkxMyAxMjYuMjE1IDEwOS4xMzYgMTI2LjA3NSAxMDguNDQxIDEyNS43OTZDMTA3Ljc1NCAxMjUuNTA5IDEwNy4xNjcgMTI1LjExMiAxMDYuNjggMTI0LjYwNEMxMDYuMiAxMjQuMDk1IDEwNS44MzEgMTIzLjQ5NyAxMDUuNTczIDEyMi44MUMxMDUuMzE1IDEyMi4xMjIgMTA1LjE4NyAxMjEuMzgxIDEwNS4xODcgMTIwLjU4NlYxMjAuMTU2QzEwNS4xODcgMTE5LjI0NyAxMDUuMzE5IDExOC40MjMgMTA1LjU4NCAxMTcuNjg2QzEwNS44NDkgMTE2Ljk0OCAxMDYuMjE4IDExNi4zMTggMTA2LjY5IDExNS43OTVDMTA3LjE2MyAxMTUuMjY1IDEwNy43MjIgMTE0Ljg2IDEwOC4zNjYgMTE0LjU4MUMxMDkuMDExIDExNC4zMDIgMTA5LjcwOSAxMTQuMTYyIDExMC40NjEgMTE0LjE2MkMxMTEuMjkyIDExNC4xNjIgMTEyLjAxOSAxMTQuMzAyIDExMi42NDIgMTE0LjU4MUMxMTMuMjY1IDExNC44NiAxMTMuNzggMTE1LjI1NCAxMTQuMTg4IDExNS43NjNDMTE0LjYwNCAxMTYuMjY0IDExNC45MTIgMTE2Ljg2MiAxMTUuMTEyIDExNy41NTdDMTE1LjMyIDExOC4yNTEgMTE1LjQyNCAxMTkuMDE4IDExNS40MjQgMTE5Ljg1NVYxMjAuOTYySDEwNi40NDNWMTE5LjEwNEgxMTIuODY3VjExOC44OTlDMTEyLjg1MyAxMTguNDM0IDExMi43NiAxMTcuOTk3IDExMi41ODggMTE3LjU4OUMxMTIuNDIzIDExNy4xODEgMTEyLjE2OSAxMTYuODUxIDExMS44MjUgMTE2LjYwMUMxMTEuNDgxIDExNi4zNSAxMTEuMDIzIDExNi4yMjUgMTEwLjQ1IDExNi4yMjVDMTEwLjAyMSAxMTYuMjI1IDEwOS42MzcgMTE2LjMxOCAxMDkuMzAxIDExNi41MDRDMTA4Ljk3MSAxMTYuNjgzIDEwOC42OTYgMTE2Ljk0NCAxMDguNDc0IDExNy4yODhDMTA4LjI1MiAxMTcuNjMyIDEwOC4wOCAxMTguMDQ3IDEwNy45NTggMTE4LjUzNEMxMDcuODQzIDExOS4wMTQgMTA3Ljc4NiAxMTkuNTU1IDEwNy43ODYgMTIwLjE1NlYxMjAuNTg2QzEwNy43ODYgMTIxLjA5NCAxMDcuODU0IDEyMS41NjcgMTA3Ljk5IDEyMi4wMDRDMTA4LjEzMyAxMjIuNDM0IDEwOC4zNDEgMTIyLjgxIDEwOC42MTMgMTIzLjEzMkMxMDguODg1IDEyMy40NTQgMTA5LjIxNSAxMjMuNzA4IDEwOS42MDIgMTIzLjg5NUMxMDkuOTg4IDEyNC4wNzQgMTEwLjQyOSAxMjQuMTYzIDExMC45MjMgMTI0LjE2M0MxMTEuNTQ2IDEyNC4xNjMgMTEyLjEwMSAxMjQuMDM4IDExMi41ODggMTIzLjc4N0MxMTMuMDc1IDEyMy41MzYgMTEzLjQ5NyAxMjMuMTgyIDExMy44NTUgMTIyLjcyNEwxMTUuMjIgMTI0LjA0NUMxMTQuOTY5IDEyNC40MSAxMTQuNjQzIDEyNC43NjEgMTE0LjI0MiAxMjUuMDk4QzExMy44NDEgMTI1LjQyNyAxMTMuMzUxIDEyNS42OTYgMTEyLjc3MSAxMjUuOTAzQzExMi4xOTggMTI2LjExMSAxMTEuNTMyIDEyNi4yMTUgMTEwLjc3MiAxMjYuMjE1Wk0xMjAuMjYxIDExNi44NThWMTI2SDExNy42NzJWMTE0LjM3N0gxMjAuMTFMMTIwLjI2MSAxMTYuODU4Wk0xMTkuNzk5IDExOS43NTlMMTE4Ljk2MSAxMTkuNzQ4QzExOC45NjggMTE4LjkyNCAxMTkuMDgzIDExOC4xNjkgMTE5LjMwNSAxMTcuNDgxQzExOS41MzQgMTE2Ljc5NCAxMTkuODQ5IDExNi4yMDMgMTIwLjI1IDExNS43MDlDMTIwLjY1OCAxMTUuMjE1IDEyMS4xNDUgMTE0LjgzNSAxMjEuNzExIDExNC41N0MxMjIuMjc3IDExNC4yOTggMTIyLjkwNyAxMTQuMTYyIDEyMy42MDIgMTE0LjE2MkMxMjQuMTYgMTE0LjE2MiAxMjQuNjY1IDExNC4yNDEgMTI1LjExNiAxMTQuMzk4QzEyNS41NzUgMTE0LjU0OSAxMjUuOTY1IDExNC43OTYgMTI2LjI4NyAxMTUuMTRDMTI2LjYxNyAxMTUuNDgzIDEyNi44NjcgMTE1LjkzMSAxMjcuMDM5IDExNi40ODJDMTI3LjIxMSAxMTcuMDI3IDEyNy4yOTcgMTE3LjY5NiAxMjcuMjk3IDExOC40OTFWMTI2SDEyNC42OTdWMTE4LjQ4QzEyNC42OTcgMTE3LjkyMiAxMjQuNjE1IDExNy40ODEgMTI0LjQ1IDExNy4xNTlDMTI0LjI5MyAxMTYuODMgMTI0LjA2IDExNi41OTcgMTIzLjc1MiAxMTYuNDYxQzEyMy40NTEgMTE2LjMxOCAxMjMuMDc1IDExNi4yNDYgMTIyLjYyNCAxMTYuMjQ2QzEyMi4xOCAxMTYuMjQ2IDEyMS43ODMgMTE2LjMzOSAxMjEuNDMyIDExNi41MjVDMTIxLjA4MSAxMTYuNzEyIDEyMC43ODQgMTE2Ljk2NiAxMjAuNTQgMTE3LjI4OEMxMjAuMzA0IDExNy42MSAxMjAuMTIxIDExNy45ODMgMTE5Ljk5MiAxMTguNDA1QzExOS44NjMgMTE4LjgyOCAxMTkuNzk5IDExOS4yNzkgMTE5Ljc5OSAxMTkuNzU5Wk0xMzcuMjc5IDEyMy41OTRWMTA5LjVIMTM5Ljg3OVYxMjZIMTM3LjUyNkwxMzcuMjc5IDEyMy41OTRaTTEyOS43MTcgMTIwLjMxN1YxMjAuMDkyQzEyOS43MTcgMTE5LjIxMSAxMjkuODIxIDExOC40MDkgMTMwLjAyOCAxMTcuNjg2QzEzMC4yMzYgMTE2Ljk1NSAxMzAuNTM3IDExNi4zMjggMTMwLjkzMSAxMTUuODA2QzEzMS4zMjUgMTE1LjI3NiAxMzEuODA0IDExNC44NzEgMTMyLjM3IDExNC41OTJDMTMyLjkzNiAxMTQuMzA1IDEzMy41NzMgMTE0LjE2MiAxMzQuMjgyIDExNC4xNjJDMTM0Ljk4NCAxMTQuMTYyIDEzNS42IDExNC4yOTggMTM2LjEzIDExNC41N0MxMzYuNjYgMTE0Ljg0MiAxMzcuMTExIDExNS4yMzMgMTM3LjQ4MyAxMTUuNzQxQzEzNy44NTYgMTE2LjI0MyAxMzguMTUzIDExNi44NDQgMTM4LjM3NSAxMTcuNTQ2QzEzOC41OTcgMTE4LjI0MSAxMzguNzU1IDExOS4wMTQgMTM4Ljg0OCAxMTkuODY2VjEyMC41ODZDMTM4Ljc1NSAxMjEuNDE3IDEzOC41OTcgMTIyLjE3NiAxMzguMzc1IDEyMi44NjNDMTM4LjE1MyAxMjMuNTUxIDEzNy44NTYgMTI0LjE0NSAxMzcuNDgzIDEyNC42NDZDMTM3LjExMSAxMjUuMTQ4IDEzNi42NTYgMTI1LjUzNSAxMzYuMTE5IDEyNS44MDdDMTM1LjU4OSAxMjYuMDc5IDEzNC45NyAxMjYuMjE1IDEzNC4yNjEgMTI2LjIxNUMxMzMuNTU5IDEyNi4yMTUgMTMyLjkyNSAxMjYuMDY4IDEzMi4zNTkgMTI1Ljc3NEMxMzEuODAxIDEyNS40ODEgMTMxLjMyNSAxMjUuMDY5IDEzMC45MzEgMTI0LjUzOUMxMzAuNTM3IDEyNC4wMDkgMTMwLjIzNiAxMjMuMzg2IDEzMC4wMjggMTIyLjY3QzEyOS44MjEgMTIxLjk0NyAxMjkuNzE3IDEyMS4xNjIgMTI5LjcxNyAxMjAuMzE3Wk0xMzIuMzA2IDEyMC4wOTJWMTIwLjMxN0MxMzIuMzA2IDEyMC44NDcgMTMyLjM1MiAxMjEuMzQxIDEzMi40NDUgMTIxLjhDMTMyLjU0NiAxMjIuMjU4IDEzMi43IDEyMi42NjMgMTMyLjkwNyAxMjMuMDE0QzEzMy4xMTUgMTIzLjM1NyAxMzMuMzgzIDEyMy42MyAxMzMuNzEzIDEyMy44M0MxMzQuMDQ5IDEyNC4wMjMgMTM0LjQ1MSAxMjQuMTIgMTM0LjkxNiAxMjQuMTJDMTM1LjUwMyAxMjQuMTIgMTM1Ljk4NyAxMjMuOTkxIDEzNi4zNjYgMTIzLjczM0MxMzYuNzQ2IDEyMy40NzYgMTM3LjA0MyAxMjMuMTI4IDEzNy4yNTggMTIyLjY5MUMxMzcuNDggMTIyLjI0NyAxMzcuNjMgMTIxLjc1MyAxMzcuNzA5IDEyMS4yMDlWMTE5LjI2NUMxMzcuNjY2IDExOC44NDIgMTM3LjU3NiAxMTguNDQ4IDEzNy40NCAxMTguMDgzQzEzNy4zMTIgMTE3LjcxOCAxMzcuMTM2IDExNy4zOTkgMTM2LjkxNCAxMTcuMTI3QzEzNi42OTIgMTE2Ljg0OCAxMzYuNDE2IDExNi42MzMgMTM2LjA4NyAxMTYuNDgyQzEzNS43NjUgMTE2LjMyNSAxMzUuMzgyIDExNi4yNDYgMTM0LjkzOCAxMTYuMjQ2QzEzNC40NjUgMTE2LjI0NiAxMzQuMDY0IDExNi4zNDYgMTMzLjczNCAxMTYuNTQ3QzEzMy40MDUgMTE2Ljc0NyAxMzMuMTMzIDExNy4wMjMgMTMyLjkxOCAxMTcuMzc0QzEzMi43MSAxMTcuNzI1IDEzMi41NTYgMTE4LjEzMyAxMzIuNDU2IDExOC41OTlDMTMyLjM1NiAxMTkuMDY0IDEzMi4zMDYgMTE5LjU2MiAxMzIuMzA2IDEyMC4wOTJaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", + "description": "Facilitates user interaction by enabling navigation between dashboard states, sending RPC commands to devices, and updating device attributes or time-series data.", + "order": 7500, + "name": "Buttons" + }, + "widgetTypeFqns": [ + "action_button", + "command_button", + "power_button" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 5dce630e64..560063a2c4 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -9,6 +9,9 @@ }, "widgetTypeFqns": [ "single_switch", + "command_button", + "power_button", + "slider", "control_widgets.switch_control", "control_widgets.slide_toggle_control", "control_widgets.round_switch", diff --git a/application/src/main/data/json/system/widget_types/action_button.json b/application/src/main/data/json/system/widget_types/action_button.json new file mode 100644 index 0000000000..a73aa198e7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/action_button.json @@ -0,0 +1,29 @@ +{ + "fqn": "action_button", + "name": "Action button", + "deprecated": false, + "image": "tb-image:YWN0aW9uLWJ1dHRvbi5zdmc=:IkFjdGlvbiBidXR0b24iIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NzMgODkuMzMzM1Y4Mi4zMzMzSDY2LjgzNFY4OS4zMzMzSDcyLjY2NzNWODBINzYuMTY3M0w2NC41MDA3IDY5LjVMNTIuODM0IDgwSDU2LjMzNFY4OS4zMzMzSDYyLjE2NzNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDgwLjkzMTZIODYuMjFMODYuMTg4NSA3OC45NjU4SDg5LjY2ODlDOTAuMjU2MiA3OC45NjU4IDkwLjc1MzkgNzguODc5OSA5MS4xNjIxIDc4LjcwOEM5MS41Nzc1IDc4LjUyOSA5MS44OTI2IDc4LjI3NDcgOTIuMTA3NCA3Ny45NDUzQzkyLjMyMjMgNzcuNjA4NyA5Mi40Mjk3IDc3LjIwNDEgOTIuNDI5NyA3Ni43MzE0QzkyLjQyOTcgNzYuMjA4NyA5Mi4zMjk0IDc1Ljc4MjYgOTIuMTI4OSA3NS40NTMxQzkxLjkyODQgNzUuMTIzNyA5MS42MjA0IDc0Ljg4MzggOTEuMjA1MSA3NC43MzM0QzkwLjc5NjkgNzQuNTgzIDkwLjI3NDEgNzQuNTA3OCA4OS42MzY3IDc0LjUwNzhIODcuMDI2NFY4OEg4NC4zMzAxVjcyLjM1OTRIODkuNjM2N0M5MC40OTYxIDcyLjM1OTQgOTEuMjYyNCA3Mi40NDE3IDkxLjkzNTUgNzIuNjA2NEM5Mi42MTU5IDcyLjc3MTIgOTMuMTkyNCA3My4wMjkgOTMuNjY1IDczLjM3OTlDOTQuMTQ0OSA3My43MjM2IDk0LjUwNjUgNzQuMTYwNSA5NC43NSA3NC42OTA0Qzk1LjAwMDcgNzUuMjIwNCA5NS4xMjYgNzUuODUwNiA5NS4xMjYgNzYuNTgxMUM5NS4xMjYgNzcuMjI1NiA5NC45NzIgNzcuODE2NCA5NC42NjQxIDc4LjM1MzVDOTQuMzU2MSA3OC44ODM1IDkzLjkwMTQgNzkuMzE2NyA5My4yOTk4IDc5LjY1MzNDOTIuNjk4MiA3OS45ODk5IDkxLjk0OTkgODAuMTkwNCA5MS4wNTQ3IDgwLjI1NDlMOTAuMTk1MyA4MC45MzE2Wk05MC4wNzcxIDg4SDg1LjM2MTNMODYuNTc1MiA4NS44NjIzSDkwLjA3NzFDOTAuNjg1OSA4NS44NjIzIDkxLjE5NDMgODUuNzYyIDkxLjYwMjUgODUuNTYxNUM5Mi4wMTA3IDg1LjM1MzggOTIuMzE1MSA4NS4wNzEgOTIuNTE1NiA4NC43MTI5QzkyLjcyMzMgODQuMzQ3NyA5Mi44MjcxIDgzLjkyMTUgOTIuODI3MSA4My40MzQ2QzkyLjgyNzEgODIuOTI2MSA5Mi43Mzc2IDgyLjQ4NTcgOTIuNTU4NiA4Mi4xMTMzQzkyLjM3OTYgODEuNzMzNyA5Mi4wOTY3IDgxLjQ0MzcgOTEuNzEgODEuMjQzMkM5MS4zMjMyIDgxLjAzNTUgOTAuODE4NCA4MC45MzE2IDkwLjE5NTMgODAuOTMxNkg4Ny4xNjZMODcuMTg3NSA3OC45NjU4SDkxLjEyOTlMOTEuNzQyMiA3OS43MDdDOTIuNjAxNiA3OS43MzU3IDkzLjMwNyA3OS45MjU1IDkzLjg1ODQgODAuMjc2NEM5NC40MTcgODAuNjI3MyA5NC44MzI0IDgxLjA4MiA5NS4xMDQ1IDgxLjY0MDZDOTUuMzc2NiA4Mi4xOTkyIDk1LjUxMjcgODIuODAwOCA5NS41MTI3IDgzLjQ0NTNDOTUuNTEyNyA4NC40NDA4IDk1LjI5NDMgODUuMjc1MSA5NC44NTc0IDg1Ljk0ODJDOTQuNDI3NyA4Ni42MjE0IDkzLjgwODMgODcuMTMzNSA5Mi45OTkgODcuNDg0NEM5Mi4xODk4IDg3LjgyODEgOTEuMjE1OCA4OCA5MC4wNzcxIDg4Wk0xMDUuMjE2IDg1LjI2MDdWNzYuMzc3SDEwNy44MTVWODhIMTA1LjM2NkwxMDUuMjE2IDg1LjI2MDdaTTEwNS41ODEgODIuODQzOEwxMDYuNDUxIDgyLjgyMjNDMTA2LjQ1MSA4My42MDI5IDEwNi4zNjUgODQuMzIyNiAxMDYuMTkzIDg0Ljk4MTRDMTA2LjAyMSA4NS42MzMxIDEwNS43NTcgODYuMjAyNSAxMDUuMzk4IDg2LjY4OTVDMTA1LjA0IDg3LjE2OTMgMTA0LjU4MiA4Ny41NDUyIDEwNC4wMjMgODcuODE3NEMxMDMuNDY1IDg4LjA4MjQgMTAyLjc5NSA4OC4yMTQ4IDEwMi4wMTUgODguMjE0OEMxMDEuNDQ5IDg4LjIxNDggMTAwLjkzIDg4LjEzMjUgMTAwLjQ1NyA4Ny45Njc4Qzk5Ljk4NDQgODcuODAzMSA5OS41NzYyIDg3LjU0ODggOTkuMjMyNCA4Ny4yMDUxQzk4Ljg5NTggODYuODYxMyA5OC42MzQ0IDg2LjQxMzcgOTguNDQ4MiA4NS44NjIzQzk4LjI2MiA4NS4zMTA5IDk4LjE2ODkgODQuNjUyIDk4LjE2ODkgODMuODg1N1Y3Ni4zNzdIMTAwLjc1OFY4My45MDcyQzEwMC43NTggODQuMzI5OCAxMDAuODA4IDg0LjY4NDIgMTAwLjkwOCA4NC45NzA3QzEwMS4wMDggODUuMjUgMTAxLjE0NSA4NS40NzU2IDEwMS4zMTYgODUuNjQ3NUMxMDEuNDg4IDg1LjgxOTMgMTAxLjY4OSA4NS45NDExIDEwMS45MTggODYuMDEyN0MxMDIuMTQ3IDg2LjA4NDMgMTAyLjM5MSA4Ni4xMjAxIDEwMi42NDggODYuMTIwMUMxMDMuMzg2IDg2LjEyMDEgMTAzLjk2NiA4NS45NzY5IDEwNC4zODkgODUuNjkwNEMxMDQuODE4IDg1LjM5NjggMTA1LjEyMyA4NS4wMDI5IDEwNS4zMDIgODQuNTA4OEMxMDUuNDg4IDg0LjAxNDYgMTA1LjU4MSA4My40NTk2IDEwNS41ODEgODIuODQzOFpNMTE2LjA0NyA3Ni4zNzdWNzguMjY3NkgxMDkuNDk0Vjc2LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDczLjUzMDNIMTEzLjk3NFY4NC43ODgxQzExMy45NzQgODUuMTQ2MiAxMTQuMDI0IDg1LjQyMTkgMTE0LjEyNCA4NS42MTUyQzExNC4yMzEgODUuODAxNCAxMTQuMzc4IDg1LjkyNjggMTE0LjU2NCA4NS45OTEyQzExNC43NTEgODYuMDU1NyAxMTQuOTY5IDg2LjA4NzkgMTE1LjIyIDg2LjA4NzlDMTE1LjM5OSA4Ni4wODc5IDExNS41NzEgODYuMDc3MSAxMTUuNzM1IDg2LjA1NTdDMTE1LjkgODYuMDM0MiAxMTYuMDMzIDg2LjAxMjcgMTE2LjEzMyA4NS45OTEyTDExNi4xNDQgODcuOTY3OEMxMTUuOTI5IDg4LjAzMjIgMTE1LjY3OCA4OC4wODk1IDExNS4zOTIgODguMTM5NkMxMTUuMTEyIDg4LjE4OTggMTE0Ljc5IDg4LjIxNDggMTE0LjQyNSA4OC4yMTQ4QzExMy44MyA4OC4yMTQ4IDExMy4zMDQgODguMTExIDExMi44NDYgODcuOTAzM0MxMTIuMzg3IDg3LjY4ODUgMTEyLjAyOSA4Ny4zNDExIDExMS43NzEgODYuODYxM0MxMTEuNTE0IDg2LjM4MTUgMTExLjM4NSA4NS43NDQxIDExMS4zODUgODQuOTQ5MlY3My41MzAzWk0xMjMuNjIzIDc2LjM3N1Y3OC4yNjc2SDExNy4wN1Y3Ni4zNzdIMTIzLjYyM1pNMTE4Ljk2MSA3My41MzAzSDEyMS41NVY4NC43ODgxQzEyMS41NSA4NS4xNDYyIDEyMS42IDg1LjQyMTkgMTIxLjcgODUuNjE1MkMxMjEuODA4IDg1LjgwMTQgMTIxLjk1NCA4NS45MjY4IDEyMi4xNDEgODUuOTkxMkMxMjIuMzI3IDg2LjA1NTcgMTIyLjU0NSA4Ni4wODc5IDEyMi43OTYgODYuMDg3OUMxMjIuOTc1IDg2LjA4NzkgMTIzLjE0NyA4Ni4wNzcxIDEyMy4zMTIgODYuMDU1N0MxMjMuNDc2IDg2LjAzNDIgMTIzLjYwOSA4Ni4wMTI3IDEyMy43MDkgODUuOTkxMkwxMjMuNzIgODcuOTY3OEMxMjMuNTA1IDg4LjAzMjIgMTIzLjI1NCA4OC4wODk1IDEyMi45NjggODguMTM5NkMxMjIuNjg4IDg4LjE4OTggMTIyLjM2NiA4OC4yMTQ4IDEyMi4wMDEgODguMjE0OEMxMjEuNDA3IDg4LjIxNDggMTIwLjg4IDg4LjExMSAxMjAuNDIyIDg3LjkwMzNDMTE5Ljk2NCA4Ny42ODg1IDExOS42MDUgODcuMzQxMSAxMTkuMzQ4IDg2Ljg2MTNDMTE5LjA5IDg2LjM4MTUgMTE4Ljk2MSA4NS43NDQxIDExOC45NjEgODQuOTQ5MlY3My41MzAzWk0xMjUuMTE5IDgyLjMxNzRWODIuMDcwM0MxMjUuMTE5IDgxLjIzMjQgMTI1LjI0MSA4MC40NTU0IDEyNS40ODQgNzkuNzM5M0MxMjUuNzI4IDc5LjAxNiAxMjYuMDc5IDc4LjM4OTMgMTI2LjUzNyA3Ny44NTk0QzEyNy4wMDMgNzcuMzIyMyAxMjcuNTY4IDc2LjkwNjkgMTI4LjIzNCA3Ni42MTMzQzEyOC45MDggNzYuMzEyNSAxMjkuNjY3IDc2LjE2MjEgMTMwLjUxMiA3Ni4xNjIxQzEzMS4zNjQgNzYuMTYyMSAxMzIuMTIzIDc2LjMxMjUgMTMyLjc4OSA3Ni42MTMzQzEzMy40NjIgNzYuOTA2OSAxMzQuMDMyIDc3LjMyMjMgMTM0LjQ5NyA3Ny44NTk0QzEzNC45NjMgNzguMzg5MyAxMzUuMzE3IDc5LjAxNiAxMzUuNTYxIDc5LjczOTNDMTM1LjgwNCA4MC40NTU0IDEzNS45MjYgODEuMjMyNCAxMzUuOTI2IDgyLjA3MDNWODIuMzE3NEMxMzUuOTI2IDgzLjE1NTMgMTM1LjgwNCA4My45MzIzIDEzNS41NjEgODQuNjQ4NEMxMzUuMzE3IDg1LjM2NDYgMTM0Ljk2MyA4NS45OTEyIDEzNC40OTcgODYuNTI4M0MxMzQuMDMyIDg3LjA1ODMgMTMzLjQ2NiA4Ny40NzM2IDEzMi44IDg3Ljc3NDRDMTMyLjEzNCA4OC4wNjggMTMxLjM3OCA4OC4yMTQ4IDEzMC41MzMgODguMjE0OEMxMjkuNjgxIDg4LjIxNDggMTI4LjkxOCA4OC4wNjggMTI4LjI0NSA4Ny43NzQ0QzEyNy41NzkgODcuNDczNiAxMjcuMDEzIDg3LjA1ODMgMTI2LjU0OCA4Ni41MjgzQzEyNi4wODIgODUuOTkxMiAxMjUuNzI4IDg1LjM2NDYgMTI1LjQ4NCA4NC42NDg0QzEyNS4yNDEgODMuOTMyMyAxMjUuMTE5IDgzLjE1NTMgMTI1LjExOSA4Mi4zMTc0Wk0xMjcuNzA4IDgyLjA3MDNWODIuMzE3NEMxMjcuNzA4IDgyLjg0MDIgMTI3Ljc2MiA4My4zMzQzIDEyNy44NjkgODMuNzk5OEMxMjcuOTc3IDg0LjI2NTMgMTI4LjE0NSA4NC42NzM1IDEyOC4zNzQgODUuMDI0NEMxMjguNjAzIDg1LjM3NTMgMTI4Ljg5NyA4NS42NTEgMTI5LjI1NSA4NS44NTE2QzEyOS42MTMgODYuMDUyMSAxMzAuMDM5IDg2LjE1MjMgMTMwLjUzMyA4Ni4xNTIzQzEzMS4wMTMgODYuMTUyMyAxMzEuNDI4IDg2LjA1MjEgMTMxLjc3OSA4NS44NTE2QzEzMi4xMzcgODUuNjUxIDEzMi40MzEgODUuMzc1MyAxMzIuNjYgODUuMDI0NEMxMzIuODg5IDg0LjY3MzUgMTMzLjA1OCA4NC4yNjUzIDEzMy4xNjUgODMuNzk5OEMxMzMuMjggODMuMzM0MyAxMzMuMzM3IDgyLjg0MDIgMTMzLjMzNyA4Mi4zMTc0VjgyLjA3MDNDMTMzLjMzNyA4MS41NTQ3IDEzMy4yOCA4MS4wNjc3IDEzMy4xNjUgODAuNjA5NEMxMzMuMDU4IDgwLjE0MzkgMTMyLjg4NiA3OS43MzIxIDEzMi42NDkgNzkuMzc0QzEzMi40MiA3OS4wMTYgMTMyLjEyNyA3OC43MzY3IDEzMS43NjkgNzguNTM2MUMxMzEuNDE4IDc4LjMyODUgMTMwLjk5OSA3OC4yMjQ2IDEzMC41MTIgNzguMjI0NkMxMzAuMDI1IDc4LjIyNDYgMTI5LjYwMiA3OC4zMjg1IDEyOS4yNDQgNzguNTM2MUMxMjguODkzIDc4LjczNjcgMTI4LjYwMyA3OS4wMTYgMTI4LjM3NCA3OS4zNzRDMTI4LjE0NSA3OS43MzIxIDEyNy45NzcgODAuMTQzOSAxMjcuODY5IDgwLjYwOTRDMTI3Ljc2MiA4MS4wNjc3IDEyNy43MDggODEuNTU0NyAxMjcuNzA4IDgyLjA3MDNaTTE0MC45MTMgNzguODU4NFY4OEgxMzguMzI0Vjc2LjM3N0gxNDAuNzYzTDE0MC45MTMgNzguODU4NFpNMTQwLjQ1MSA4MS43NTg4TDEzOS42MTMgODEuNzQ4QzEzOS42MiA4MC45MjQ1IDEzOS43MzUgODAuMTY4OSAxMzkuOTU3IDc5LjQ4MTRDMTQwLjE4NiA3OC43OTM5IDE0MC41MDEgNzguMjAzMSAxNDAuOTAyIDc3LjcwOUMxNDEuMzExIDc3LjIxNDggMTQxLjc5OCA3Ni44MzUzIDE0Mi4zNjMgNzYuNTcwM0MxNDIuOTI5IDc2LjI5ODIgMTQzLjU1OSA3Ni4xNjIxIDE0NC4yNTQgNzYuMTYyMUMxNDQuODEyIDc2LjE2MjEgMTQ1LjMxNyA3Ni4yNDA5IDE0NS43NjkgNzYuMzk4NEMxNDYuMjI3IDc2LjU0ODggMTQ2LjYxNyA3Ni43OTU5IDE0Ni45MzkgNzcuMTM5NkMxNDcuMjY5IDc3LjQ4MzQgMTQ3LjUyIDc3LjkzMSAxNDcuNjkxIDc4LjQ4MjRDMTQ3Ljg2MyA3OS4wMjY3IDE0Ny45NDkgNzkuNjk2MyAxNDcuOTQ5IDgwLjQ5MTJWODhIMTQ1LjM1VjgwLjQ4MDVDMTQ1LjM1IDc5LjkyMTkgMTQ1LjI2NyA3OS40ODE0IDE0NS4xMDMgNzkuMTU5MkMxNDQuOTQ1IDc4LjgyOTggMTQ0LjcxMiA3OC41OTcgMTQ0LjQwNCA3OC40NjA5QzE0NC4xMDQgNzguMzE3NyAxNDMuNzI4IDc4LjI0NjEgMTQzLjI3NiA3OC4yNDYxQzE0Mi44MzIgNzguMjQ2MSAxNDIuNDM1IDc4LjMzOTIgMTQyLjA4NCA3OC41MjU0QzE0MS43MzMgNzguNzExNiAxNDEuNDM2IDc4Ljk2NTggMTQxLjE5MiA3OS4yODgxQzE0MC45NTYgNzkuNjEwNCAxNDAuNzczIDc5Ljk4MjcgMTQwLjY0NSA4MC40MDUzQzE0MC41MTYgODAuODI3OCAxNDAuNDUxIDgxLjI3OSAxNDAuNDUxIDgxLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", + "description": "Facilitates single-click navigation to other dashboards, states, or custom actions. Configurable settings allow for on-click action definition and conditions for button activation or deactivation. It offers various layouts and custom styling options for different states.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container tb-markdown-widget {\n height: 100%;\n display: block;\n}\n\n#container tb-markdown-widget .tb-markdown-view {\n height: 100%;\n overflow: auto;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.actionSources = function() {\n return {\n 'click': {\n name: 'widget-action.click',\n multiple: false\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n maxDatasources: 1,\n maxDataKeys: 0,\n singleEntity: true,\n previewWidth: '200px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n hideDataSettings: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-action-button-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-action-button-basic-config", + "defaultConfig": "{\"datasources\":[],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#FFFFFF01\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Action button\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":false,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false,\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"borderRadius\":\"4px\",\"configMode\":\"basic\"}" + }, + "tags": [ + "button", + "action", + "navigation", + "navigate", + "dashboard state" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/command_button.json b/application/src/main/data/json/system/widget_types/command_button.json new file mode 100644 index 0000000000..dcfeb108f2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/command_button.json @@ -0,0 +1,39 @@ +{ + "fqn": "command_button", + "name": "Command button", + "deprecated": false, + "image": "tb-image:Y29tbWFuZC1idXR0b24uc3Zn:IkNvbW1hbmQgYnV0dG9uIiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgNzNWNzUuMzMzM0g3NS41MjEzTDY0LjMzMyA4Ni41MjE3TDY1Ljk3OCA4OC4xNjY3TDc3LjE2NjMgNzYuOTc4M1Y4N0g3OS40OTk3VjczSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDgzLjk3MTdDMTAwLjU2NCA4My42NDk0IDEwMC41MTQgODMuMzYzIDEwMC40MTQgODMuMTEyM0MxMDAuMzIxIDgyLjg2MTcgMTAwLjE1MyA4Mi42MzI1IDk5LjkwOTIgODIuNDI0OEM5OS42NjU3IDgyLjIxNzEgOTkuMzIxOSA4Mi4wMTY2IDk4Ljg3NzkgODEuODIzMkM5OC40NDExIDgxLjYyMjcgOTcuODgyNSA4MS40MTg2IDk3LjIwMjEgODEuMjEwOUM5Ni40NTc0IDgwLjk4MTggOTUuNzY5OSA4MC43Mjc1IDk1LjEzOTYgODAuNDQ4MkM5NC41MTY2IDgwLjE2MTggOTMuOTcyMyA3OS44MzI0IDkzLjUwNjggNzkuNDZDOTMuMDQxMyA3OS4wODA0IDkyLjY3OTcgNzguNjQ3MSA5Mi40MjE5IDc4LjE2MDJDOTIuMTY0MSA3Ny42NjYgOTIuMDM1MiA3Ny4wOTY3IDkyLjAzNTIgNzYuNDUyMUM5Mi4wMzUyIDc1LjgxNDggOTIuMTY3NiA3NS4yMzQ3IDkyLjQzMjYgNzQuNzExOUM5Mi43MDQ4IDc0LjE4OTEgOTMuMDg3OSA3My43MzggOTMuNTgyIDczLjM1ODRDOTQuMDgzMyA3Mi45NzE3IDk0LjY3NDIgNzIuNjc0NSA5NS4zNTQ1IDcyLjQ2NjhDOTYuMDM0OCA3Mi4yNTIgOTYuNzg2OCA3Mi4xNDQ1IDk3LjYxMDQgNzIuMTQ0NUM5OC43NzA1IDcyLjE0NDUgOTkuNzY5NSA3Mi4zNTk0IDEwMC42MDcgNzIuNzg5MUMxMDEuNDUyIDczLjIxODggMTAyLjEwMSA3My43OTUyIDEwMi41NTIgNzQuNTE4NkMxMDMuMDEgNzUuMjQxOSAxMDMuMjM5IDc2LjA0MDQgMTAzLjIzOSA3Ni45MTQxSDEwMC41NjRDMTAwLjU2NCA3Ni4zOTg0IDEwMC40NTMgNzUuOTQzNyAxMDAuMjMxIDc1LjU0OThDMTAwLjAxNyA3NS4xNDg4IDk5LjY4NzIgNzQuODMzNyA5OS4yNDMyIDc0LjYwNDVDOTguODA2MyA3NC4zNzUzIDk4LjI1MTMgNzQuMjYwNyA5Ny41NzgxIDc0LjI2MDdDOTYuOTQwOCA3NC4yNjA3IDk2LjQxMDggNzQuMzU3NCA5NS45ODgzIDc0LjU1MDhDOTUuNTY1OCA3NC43NDQxIDk1LjI1MDcgNzUuMDA1NSA5NS4wNDMgNzUuMzM1Qzk0LjgzNTMgNzUuNjY0NCA5NC43MzE0IDc2LjAzNjggOTQuNzMxNCA3Ni40NTIxQzk0LjczMTQgNzYuNzQ1OCA5NC43OTk1IDc3LjAxNDMgOTQuOTM1NSA3Ny4yNTc4Qzk1LjA3MTYgNzcuNDk0MSA5NS4yNzkzIDc3LjcxNjEgOTUuNTU4NiA3Ny45MjM4Qzk1LjgzNzkgNzguMTI0MyA5Ni4xODg4IDc4LjMxNDEgOTYuNjExMyA3OC40OTMyQzk3LjAzMzkgNzguNjcyMiA5Ny41MzE2IDc4Ljg0NDEgOTguMTA0NSA3OS4wMDg4Qzk4Ljk3MSA3OS4yNjY2IDk5LjcyNjYgNzkuNTUzMSAxMDAuMzcxIDc5Ljg2ODJDMTAxLjAxNiA4MC4xNzYxIDEwMS41NTMgODAuNTI3IDEwMS45ODIgODAuOTIwOUMxMDIuNDEyIDgxLjMxNDggMTAyLjczNCA4MS43NjI0IDEwMi45NDkgODIuMjYzN0MxMDMuMTY0IDgyLjc1NzggMTAzLjI3MSA4My4zMiAxMDMuMjcxIDgzLjk1MDJDMTAzLjI3MSA4NC42MDkgMTAzLjEzOSA4NS4yMDM1IDEwMi44NzQgODUuNzMzNEMxMDIuNjA5IDg2LjI1NjIgMTAyLjIyOSA4Ni43MDM4IDEwMS43MzUgODcuMDc2MkMxMDEuMjQ4IDg3LjQ0MTQgMTAwLjY2MSA4Ny43MjQzIDk5Ljk3MzYgODcuOTI0OEM5OS4yOTMzIDg4LjExODIgOTguNTM0MiA4OC4yMTQ4IDk3LjY5NjMgODguMjE0OEM5Ni45NDQzIDg4LjIxNDggOTYuMjAzMSA4OC4xMTQ2IDk1LjQ3MjcgODcuOTE0MUM5NC43NDkzIDg3LjcxMzUgOTQuMDkwNSA4Ny40MDkyIDkzLjQ5NjEgODcuMDAxQzkyLjkwMTcgODYuNTg1NiA5Mi40MjkgODYuMDcgOTIuMDc4MSA4NS40NTQxQzkxLjcyNzIgODQuODMxMSA5MS41NTE4IDg0LjEwNDIgOTEuNTUxOCA4My4yNzM0SDk0LjI0OEM5NC4yNDggODMuNzgxOSA5NC4zMzQgODQuMjE1MiA5NC41MDU5IDg0LjU3MzJDOTQuNjg0OSA4NC45MzEzIDk0LjkzMiA4NS4yMjQ5IDk1LjI0NzEgODUuNDU0MUM5NS41NjIyIDg1LjY3NjEgOTUuOTI3NCA4NS44NDA4IDk2LjM0MjggODUuOTQ4MkM5Ni43NjUzIDg2LjA1NTcgOTcuMjE2NSA4Ni4xMDk0IDk3LjY5NjMgODYuMTA5NEM5OC4zMjY1IDg2LjEwOTQgOTguODUyOSA4Ni4wMTk5IDk5LjI3NTQgODUuODQwOEM5OS43MDUxIDg1LjY2MTggMTAwLjAyNyA4NS40MTExIDEwMC4yNDIgODUuMDg4OUMxMDAuNDU3IDg0Ljc2NjYgMTAwLjU2NCA4NC4zOTQyIDEwMC41NjQgODMuOTcxN1pNMTEwLjc3MiA4OC4yMTQ4QzEwOS45MTMgODguMjE0OCAxMDkuMTM2IDg4LjA3NTIgMTA4LjQ0MSA4Ny43OTU5QzEwNy43NTQgODcuNTA5NCAxMDcuMTY3IDg3LjExMiAxMDYuNjggODYuNjAzNUMxMDYuMiA4Ni4wOTUxIDEwNS44MzEgODUuNDk3MSAxMDUuNTczIDg0LjgwOTZDMTA1LjMxNSA4NC4xMjIxIDEwNS4xODcgODMuMzgwOSAxMDUuMTg3IDgyLjU4NTlWODIuMTU2MkMxMDUuMTg3IDgxLjI0NjcgMTA1LjMxOSA4MC40MjMyIDEwNS41ODQgNzkuNjg1NUMxMDUuODQ5IDc4Ljk0NzkgMTA2LjIxOCA3OC4zMTc3IDEwNi42OSA3Ny43OTQ5QzEwNy4xNjMgNzcuMjY1IDEwNy43MjIgNzYuODYwNCAxMDguMzY2IDc2LjU4MTFDMTA5LjAxMSA3Ni4zMDE4IDEwOS43MDkgNzYuMTYyMSAxMTAuNDYxIDc2LjE2MjFDMTExLjI5MiA3Ni4xNjIxIDExMi4wMTkgNzYuMzAxOCAxMTIuNjQyIDc2LjU4MTFDMTEzLjI2NSA3Ni44NjA0IDExMy43OCA3Ny4yNTQyIDExNC4xODggNzcuNzYyN0MxMTQuNjA0IDc4LjI2NCAxMTQuOTEyIDc4Ljg2MiAxMTUuMTEyIDc5LjU1NjZDMTE1LjMyIDgwLjI1MTMgMTE1LjQyNCA4MS4wMTc2IDExNS40MjQgODEuODU1NVY4Mi45NjE5SDEwNi40NDNWODEuMTAzNUgxMTIuODY3VjgwLjg5OTRDMTEyLjg1MyA4MC40MzM5IDExMi43NiA3OS45OTcxIDExMi41ODggNzkuNTg4OUMxMTIuNDIzIDc5LjE4MDcgMTEyLjE2OSA3OC44NTEyIDExMS44MjUgNzguNjAwNkMxMTEuNDgxIDc4LjM0OTkgMTExLjAyMyA3OC4yMjQ2IDExMC40NSA3OC4yMjQ2QzExMC4wMjEgNzguMjI0NiAxMDkuNjM3IDc4LjMxNzcgMTA5LjMwMSA3OC41MDM5QzEwOC45NzEgNzguNjgyOSAxMDguNjk2IDc4Ljk0NDMgMTA4LjQ3NCA3OS4yODgxQzEwOC4yNTIgNzkuNjMxOCAxMDguMDggODAuMDQ3MiAxMDcuOTU4IDgwLjUzNDJDMTA3Ljg0MyA4MS4wMTQgMTA3Ljc4NiA4MS41NTQ3IDEwNy43ODYgODIuMTU2MlY4Mi41ODU5QzEwNy43ODYgODMuMDk0NCAxMDcuODU0IDgzLjU2NzEgMTA3Ljk5IDg0LjAwMzlDMTA4LjEzMyA4NC40MzM2IDEwOC4zNDEgODQuODA5NiAxMDguNjEzIDg1LjEzMThDMTA4Ljg4NSA4NS40NTQxIDEwOS4yMTUgODUuNzA4MyAxMDkuNjAyIDg1Ljg5NDVDMTA5Ljk4OCA4Ni4wNzM2IDExMC40MjkgODYuMTYzMSAxMTAuOTIzIDg2LjE2MzFDMTExLjU0NiA4Ni4xNjMxIDExMi4xMDEgODYuMDM3OCAxMTIuNTg4IDg1Ljc4NzFDMTEzLjA3NSA4NS41MzY1IDExMy40OTcgODUuMTgyIDExMy44NTUgODQuNzIzNkwxMTUuMjIgODYuMDQ0OUMxMTQuOTY5IDg2LjQxMDIgMTE0LjY0MyA4Ni43NjExIDExNC4yNDIgODcuMDk3N0MxMTMuODQxIDg3LjQyNzEgMTEzLjM1MSA4Ny42OTU2IDExMi43NzEgODcuOTAzM0MxMTIuMTk4IDg4LjExMSAxMTEuNTMyIDg4LjIxNDggMTEwLjc3MiA4OC4yMTQ4Wk0xMjAuMjYxIDc4Ljg1ODRWODhIMTE3LjY3MlY3Ni4zNzdIMTIwLjExTDEyMC4yNjEgNzguODU4NFpNMTE5Ljc5OSA4MS43NTg4TDExOC45NjEgODEuNzQ4QzExOC45NjggODAuOTI0NSAxMTkuMDgzIDgwLjE2ODkgMTE5LjMwNSA3OS40ODE0QzExOS41MzQgNzguNzkzOSAxMTkuODQ5IDc4LjIwMzEgMTIwLjI1IDc3LjcwOUMxMjAuNjU4IDc3LjIxNDggMTIxLjE0NSA3Ni44MzUzIDEyMS43MTEgNzYuNTcwM0MxMjIuMjc3IDc2LjI5ODIgMTIyLjkwNyA3Ni4xNjIxIDEyMy42MDIgNzYuMTYyMUMxMjQuMTYgNzYuMTYyMSAxMjQuNjY1IDc2LjI0MDkgMTI1LjExNiA3Ni4zOTg0QzEyNS41NzUgNzYuNTQ4OCAxMjUuOTY1IDc2Ljc5NTkgMTI2LjI4NyA3Ny4xMzk2QzEyNi42MTcgNzcuNDgzNCAxMjYuODY3IDc3LjkzMSAxMjcuMDM5IDc4LjQ4MjRDMTI3LjIxMSA3OS4wMjY3IDEyNy4yOTcgNzkuNjk2MyAxMjcuMjk3IDgwLjQ5MTJWODhIMTI0LjY5N1Y4MC40ODA1QzEyNC42OTcgNzkuOTIxOSAxMjQuNjE1IDc5LjQ4MTQgMTI0LjQ1IDc5LjE1OTJDMTI0LjI5MyA3OC44Mjk4IDEyNC4wNiA3OC41OTcgMTIzLjc1MiA3OC40NjA5QzEyMy40NTEgNzguMzE3NyAxMjMuMDc1IDc4LjI0NjEgMTIyLjYyNCA3OC4yNDYxQzEyMi4xOCA3OC4yNDYxIDEyMS43ODMgNzguMzM5MiAxMjEuNDMyIDc4LjUyNTRDMTIxLjA4MSA3OC43MTE2IDEyMC43ODQgNzguOTY1OCAxMjAuNTQgNzkuMjg4MUMxMjAuMzA0IDc5LjYxMDQgMTIwLjEyMSA3OS45ODI3IDExOS45OTIgODAuNDA1M0MxMTkuODYzIDgwLjgyNzggMTE5Ljc5OSA4MS4yNzkgMTE5Ljc5OSA4MS43NTg4Wk0xMzcuMjc5IDg1LjU5MzhWNzEuNUgxMzkuODc5Vjg4SDEzNy41MjZMMTM3LjI3OSA4NS41OTM4Wk0xMjkuNzE3IDgyLjMxNzRWODIuMDkxOEMxMjkuNzE3IDgxLjIxMDkgMTI5LjgyMSA4MC40MDg5IDEzMC4wMjggNzkuNjg1NUMxMzAuMjM2IDc4Ljk1NTEgMTMwLjUzNyA3OC4zMjg1IDEzMC45MzEgNzcuODA1N0MxMzEuMzI1IDc3LjI3NTcgMTMxLjgwNCA3Ni44NzExIDEzMi4zNyA3Ni41OTE4QzEzMi45MzYgNzYuMzA1MyAxMzMuNTczIDc2LjE2MjEgMTM0LjI4MiA3Ni4xNjIxQzEzNC45ODQgNzYuMTYyMSAxMzUuNiA3Ni4yOTgyIDEzNi4xMyA3Ni41NzAzQzEzNi42NiA3Ni44NDI0IDEzNy4xMTEgNzcuMjMyNyAxMzcuNDgzIDc3Ljc0MTJDMTM3Ljg1NiA3OC4yNDI1IDEzOC4xNTMgNzguODQ0MSAxMzguMzc1IDc5LjU0NTlDMTM4LjU5NyA4MC4yNDA2IDEzOC43NTUgODEuMDE0IDEzOC44NDggODEuODY2MlY4Mi41ODU5QzEzOC43NTUgODMuNDE2NyAxMzguNTk3IDg0LjE3NTggMTM4LjM3NSA4NC44NjMzQzEzOC4xNTMgODUuNTUwOCAxMzcuODU2IDg2LjE0NTIgMTM3LjQ4MyA4Ni42NDY1QzEzNy4xMTEgODcuMTQ3OCAxMzYuNjU2IDg3LjUzNDUgMTM2LjExOSA4Ny44MDY2QzEzNS41ODkgODguMDc4OCAxMzQuOTcgODguMjE0OCAxMzQuMjYxIDg4LjIxNDhDMTMzLjU1OSA4OC4yMTQ4IDEzMi45MjUgODguMDY4IDEzMi4zNTkgODcuNzc0NEMxMzEuODAxIDg3LjQ4MDggMTMxLjMyNSA4Ny4wNjkgMTMwLjkzMSA4Ni41MzkxQzEzMC41MzcgODYuMDA5MSAxMzAuMjM2IDg1LjM4NjEgMTMwLjAyOCA4NC42Njk5QzEyOS44MjEgODMuOTQ2NiAxMjkuNzE3IDgzLjE2MjQgMTI5LjcxNyA4Mi4zMTc0Wk0xMzIuMzA2IDgyLjA5MThWODIuMzE3NEMxMzIuMzA2IDgyLjg0NzMgMTMyLjM1MiA4My4zNDE1IDEzMi40NDUgODMuNzk5OEMxMzIuNTQ2IDg0LjI1ODEgMTMyLjcgODQuNjYyOCAxMzIuOTA3IDg1LjAxMzdDMTMzLjExNSA4NS4zNTc0IDEzMy4zODMgODUuNjI5NiAxMzMuNzEzIDg1LjgzMDFDMTM0LjA0OSA4Ni4wMjM0IDEzNC40NTEgODYuMTIwMSAxMzQuOTE2IDg2LjEyMDFDMTM1LjUwMyA4Ni4xMjAxIDEzNS45ODcgODUuOTkxMiAxMzYuMzY2IDg1LjczMzRDMTM2Ljc0NiA4NS40NzU2IDEzNy4wNDMgODUuMTI4MyAxMzcuMjU4IDg0LjY5MTRDMTM3LjQ4IDg0LjI0NzQgMTM3LjYzIDgzLjc1MzMgMTM3LjcwOSA4My4yMDlWODEuMjY0NkMxMzcuNjY2IDgwLjg0MjEgMTM3LjU3NiA4MC40NDgyIDEzNy40NCA4MC4wODNDMTM3LjMxMiA3OS43MTc4IDEzNy4xMzYgNzkuMzk5MSAxMzYuOTE0IDc5LjEyN0MxMzYuNjkyIDc4Ljg0NzcgMTM2LjQxNiA3OC42MzI4IDEzNi4wODcgNzguNDgyNEMxMzUuNzY1IDc4LjMyNDkgMTM1LjM4MiA3OC4yNDYxIDEzNC45MzggNzguMjQ2MUMxMzQuNDY1IDc4LjI0NjEgMTM0LjA2NCA3OC4zNDY0IDEzMy43MzQgNzguNTQ2OUMxMzMuNDA1IDc4Ljc0NzQgMTMzLjEzMyA3OS4wMjMxIDEzMi45MTggNzkuMzc0QzEzMi43MSA3OS43MjQ5IDEzMi41NTYgODAuMTMzMSAxMzIuNDU2IDgwLjU5ODZDMTMyLjM1NiA4MS4wNjQxIDEzMi4zMDYgODEuNTYxOCAxMzIuMzA2IDgyLjA5MThaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", + "description": "Allows single-click commands to devices or updates to attributes/time-series. Settings enable definition of the on-click action and condition when the button is disabled. Supports multiple layouts and custom styles for different states.", + "descriptor": { + "type": "rpc", + "sizeX": 3, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '200px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-command-button-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-command-button-basic-config", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgba(255, 255, 255, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Command button\",\"dropShadow\":false,\"enableFullscreen\":false,\"widgetStyle\":{},\"actions\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"configMode\":\"basic\",\"borderRadius\":\"4px\"}" + }, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request", + "button", + "update attribute", + "set attribute", + "add time-series" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_button.json b/application/src/main/data/json/system/widget_types/power_button.json new file mode 100644 index 0000000000..d90439d82f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_button.json @@ -0,0 +1,36 @@ +{ + "fqn": "power_button", + "name": "Power button", + "deprecated": false, + "image": "tb-image:cG93ZXItYnV0dG9uLnN2Zw==:IlBvd2VyIGJ1dHRvbiIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2lfNDU4OF84ODQyMCkiPgo8cGF0aCBkPSJNMTQ2LjUgODBDMTQ2LjUgMTA1LjEyOSAxMjUuNjgxIDEyNS41IDEwMCAxMjUuNUM3NC4zMTg4IDEyNS41IDUzLjUgMTA1LjEyOSA1My41IDgwQzUzLjUgNTQuODcxIDc0LjMxODggMzQuNSAxMDAgMzQuNUMxMjUuNjgxIDM0LjUgMTQ2LjUgNTQuODcxIDE0Ni41IDgwWiIgZmlsbD0iIzNGNTJERCIvPgo8L2c+CjxwYXRoIGQ9Ik0xNDUuNSA4MEMxNDUuNSAxMDQuNTU2IDEyNS4xNSAxMjQuNSAxMDAgMTI0LjVDNzQuODUwNCAxMjQuNSA1NC41IDEwNC41NTYgNTQuNSA4MEM1NC41IDU1LjQ0MzcgNzQuODUwNCAzNS41IDEwMCAzNS41QzEyNS4xNSAzNS41IDE0NS41IDU1LjQ0MzcgMTQ1LjUgODBaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjFfZF80NTg4Xzg4NDIwKSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAwIDEzNS41QzEzMS4yMDQgMTM1LjUgMTU2LjUgMTEwLjY1MiAxNTYuNSA4MEMxNTYuNSA0OS4zNDgyIDEzMS4yMDQgMjQuNSAxMDAgMjQuNUM2OC43OTU5IDI0LjUgNDMuNSA0OS4zNDgyIDQzLjUgODBDNDMuNSAxMTAuNjUyIDY4Ljc5NTkgMTM1LjUgMTAwIDEzNS41Wk0xMDAgMTI1LjVDMTI1LjY4MSAxMjUuNSAxNDYuNSAxMDUuMTI5IDE0Ni41IDgwQzE0Ni41IDU0Ljg3MSAxMjUuNjgxIDM0LjUgMTAwIDM0LjVDNzQuMzE4OCAzNC41IDUzLjUgNTQuODcxIDUzLjUgODBDNTMuNSAxMDUuMTI5IDc0LjMxODggMTI1LjUgMTAwIDEyNS41WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzQ1ODhfODg0MjApIi8+CjwvZz4KPHBhdGggZD0iTTk4LjM1NzQgNzkuMTg1NVY4MC4xNzM4Qzk4LjM1NzQgODEuMzQ4MyA5OC4yMTA2IDgyLjQwMSA5Ny45MTcgODMuMzMyQzk3LjYyMzQgODQuMjYzIDk3LjIwMDggODUuMDU0NCA5Ni42NDk0IDg1LjcwNjFDOTYuMDk4IDg2LjM1NzcgOTUuNDM1NSA4Ni44NTU1IDk0LjY2MjEgODcuMTk5MkM5My44OTU4IDg3LjU0MyA5My4wMzY1IDg3LjcxNDggOTIuMDg0IDg3LjcxNDhDOTEuMTYwMiA4Ny43MTQ4IDkwLjMxMTUgODcuNTQzIDg5LjUzODEgODcuMTk5MkM4OC43NzE4IDg2Ljg1NTUgODguMTA1OCA4Ni4zNTc3IDg3LjU0IDg1LjcwNjFDODYuOTgxNCA4NS4wNTQ0IDg2LjU0ODIgODQuMjYzIDg2LjI0MDIgODMuMzMyQzg1LjkzMjMgODIuNDAxIDg1Ljc3ODMgODEuMzQ4MyA4NS43NzgzIDgwLjE3MzhWNzkuMTg1NUM4NS43NzgzIDc4LjAxMTEgODUuOTI4NyA3Ni45NjE5IDg2LjIyOTUgNzYuMDM4MUM4Ni41Mzc0IDc1LjEwNzEgODYuOTcwNyA3NC4zMTU4IDg3LjUyOTMgNzMuNjY0MUM4OC4wODc5IDczLjAwNTIgODguNzUwMyA3Mi41MDM5IDg5LjUxNjYgNzIuMTYwMkM5MC4yOSA3MS44MTY0IDkxLjEzODcgNzEuNjQ0NSA5Mi4wNjI1IDcxLjY0NDVDOTMuMDE1IDcxLjY0NDUgOTMuODc0MyA3MS44MTY0IDk0LjY0MDYgNzIuMTYwMkM5NS40MTQxIDcyLjUwMzkgOTYuMDc2NSA3My4wMDUyIDk2LjYyNzkgNzMuNjY0MUM5Ny4xODY1IDc0LjMxNTggOTcuNjEyNiA3NS4xMDcxIDk3LjkwNjIgNzYuMDM4MUM5OC4yMDcgNzYuOTYxOSA5OC4zNTc0IDc4LjAxMTEgOTguMzU3NCA3OS4xODU1Wk05Ni4zMDU3IDgwLjE3MzhWNzkuMTY0MUM5Ni4zMDU3IDc4LjIzMzEgOTYuMjA5IDc3LjQwOTUgOTYuMDE1NiA3Ni42OTM0Qzk1LjgyOTQgNzUuOTc3MiA5NS41NTM3IDc1LjM3NTcgOTUuMTg4NSA3NC44ODg3Qzk0LjgyMzIgNzQuNDAxNyA5NC4zNzU3IDc0LjAzMjkgOTMuODQ1NyA3My43ODIyQzkzLjMyMjkgNzMuNTMxNiA5Mi43Mjg1IDczLjQwNjIgOTIuMDYyNSA3My40MDYyQzkxLjQxOCA3My40MDYyIDkwLjgzNDMgNzMuNTMxNiA5MC4zMTE1IDczLjc4MjJDODkuNzk1OSA3NC4wMzI5IDg5LjM1MTkgNzQuNDAxNyA4OC45Nzk1IDc0Ljg4ODdDODguNjE0MyA3NS4zNzU3IDg4LjMzMTQgNzUuOTc3MiA4OC4xMzA5IDc2LjY5MzRDODcuOTMwMyA3Ny40MDk1IDg3LjgzMDEgNzguMjMzMSA4Ny44MzAxIDc5LjE2NDFWODAuMTczOEM4Ny44MzAxIDgxLjExMiA4Ny45MzAzIDgxLjk0MjcgODguMTMwOSA4Mi42NjZDODguMzMxNCA4My4zODIyIDg4LjYxNzggODMuOTg3MyA4OC45OTAyIDg0LjQ4MTRDODkuMzY5OCA4NC45Njg0IDg5LjgxNzQgODUuMzM3MiA5MC4zMzMgODUuNTg3OUM5MC44NTU4IDg1LjgzODUgOTEuNDM5NSA4NS45NjM5IDkyLjA4NCA4NS45NjM5QzkyLjc1NzIgODUuOTYzOSA5My4zNTUxIDg1LjgzODUgOTMuODc3OSA4NS41ODc5Qzk0LjQwMDcgODUuMzM3MiA5NC44NDExIDg0Ljk2ODQgOTUuMTk5MiA4NC40ODE0Qzk1LjU2NDUgODMuOTg3MyA5NS44NDAyIDgzLjM4MjIgOTYuMDI2NCA4Mi42NjZDOTYuMjEyNiA4MS45NDI3IDk2LjMwNTcgODEuMTEyIDk2LjMwNTcgODAuMTczOFpNMTEzLjQ5MyA3MS44NTk0Vjg3LjVIMTExLjQwOUwxMDMuNTM1IDc1LjQzNjVWODcuNUgxMDEuNDYyVjcxLjg1OTRIMTAzLjUzNUwxMTEuNDQxIDgzLjk1NTFWNzEuODU5NEgxMTMuNDkzWiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxmaWx0ZXIgaWQ9ImZpbHRlcjBfaV80NTg4Xzg4NDIwIiB4PSI0OC41IiB5PSIzNC41IiB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4KPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0ic2hhcGUiLz4KPGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiIHJlc3VsdD0iaGFyZEFscGhhIi8+CjxmZU9mZnNldCBkeD0iLTUiIGR5PSI1Ii8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjQiLz4KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0iYXJpdGhtZXRpYyIgazI9Ii0xIiBrMz0iMSIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMC4xNSAwIi8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJzaGFwZSIgcmVzdWx0PSJlZmZlY3QxX2lubmVyU2hhZG93XzQ1ODhfODg0MjAiLz4KPC9maWx0ZXI+CjxmaWx0ZXIgaWQ9ImZpbHRlcjFfZF80NTg4Xzg4NDIwIiB4PSIzNS41IiB5PSIyMC41IiB3aWR0aD0iMTI5IiBoZWlnaHQ9IjEyNyIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4KPGZlT2Zmc2V0IGR5PSI0Ii8+CjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjQiLz4KPGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+CjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA4IDAiLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfNDU4OF84ODQyMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd180NTg4Xzg4NDIwIiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfNDU4OF84ODQyMCIgeDE9IjY1Ljg5NjQiIHkxPSIxMjQuNSIgeDI9IjEyOS41MTkiIHkyPSIzMy45MjQ4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNDQ0NDQ0MiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSJ3aGl0ZSIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Sends the command to the device or updates attribute/time-series when the user pushes the button. Widget settings will enable you to configure behavior how to fetch the initial state and what to trigger when power on/off states.", + "descriptor": { + "type": "rpc", + "sizeX": 3.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '280px',\n previewHeight: '280px',\n embedTitlePanel: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-power-button-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-power-button-basic-config", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Power\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"actions\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"1.6\"},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"titleIcon\":\"mdi:lightbulb-outline\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"configMode\":\"basic\",\"targetDevice\":null,\"titleColor\":null,\"borderRadius\":null}" + }, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request", + "power" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/progress_bar.json b/application/src/main/data/json/system/widget_types/progress_bar.json index 1a92b6d470..6e60c20942 100644 --- a/application/src/main/data/json/system/widget_types/progress_bar.json +++ b/application/src/main/data/json/system/widget_types/progress_bar.json @@ -2,7 +2,7 @@ "fqn": "progress_bar", "name": "Progress bar", "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAeFBMVEXg4ODf39/g4ODg4OAAAAD////g4OD19fUhISE/Ut3j4+OsrKzHx8eQkJB0dHQ8PDzx8fEvLy+6urrV1dWCgoJYWFienp5KSkrv7++GhoZmZmaXl5f39/epqanLy8vCwsL29vZtfORiceJ+fn7m5ubU1NTn6fve4fIbcz38AAAABXRSTlPvIL+vAC9A4IoAAASZSURBVHja7d0NT9swEIDhMji4nb/jxEk/NmCM7f//w/niQLtJyxamtdfiF6EmDkh+1Di0IojVzdWH1e25d311s7paDXD2+cy49nABDfm0gotodSmQ2woRVoVIq0KkVSHSqhBpVYi0KkRaFSKtGUijctrDv6XQwEzHgDjkXLoAiKaoAurzhxAAGGxBm2gMgE+25SHeUNE0QKbRNo/otlWQo2QNQa6xrdpD1DSqW6vyBhmtrD86RKHNGIcOyOUHZJDDEEIe1xgQdT487gGF0CNmgUXnsH+BhPzhCKDFfNh50OgQ6dgQv0HFEB2hw8QYDQo3HlqGZFf0066GDiNQFmvcMEZNEMdH2zza8WhivopwVEjX9QF7yBCVSbwFCc04YdBoy4xhkw3QYAsWjQciiNhH8OT3a8RnXo4ooRm/M3dMSN91m+QnCM+6zN9h2ShTyq4xy08X9mpcVxgswR4yfktymDsJhICbIPEA4n+GJJXTfNS48atIWQzxABKQl0YEJQDiQ/Dl1LeoDyHtONWYj41TDtDYvJMwTZA2w7ADwwNJAAQS9sqgY4NLyb1CKGCrGNeh0QY7Pq6bHvUEQaMcKh5VJkiAgAmIHfGGw7B5hQD1iMHkR4vlC5TLA+l1jeS9dlo5LdojQeYj/7rBp8xrnvz+kSOCg349WjolpES9itphBEm9BeI3OJ46omLI8qhppP2e6z28sTqvKkRaFSKtCpFWhUirQqRVIdKqEGlViLQqRFoVIq0KkVaFSKtCpFUh0qoQaVWItCpEWhUirQqR1jzER609HEbSbhT4G4g3AXOWYKrpAnYgszlIjyVXJNTl7XOEJMQ+ArXT5L3LpBQJZDYD6RGJAQED5DKoBbnN3uTfvIAAgBB7EFyBzBbQlXsTNV/EQGh/hPgWUZW7e1PglR+hJOw6PA/RfCs2Gsg5nApFYvtO1L2A8xC1n3ne6DWQRewKEfQGSM7FeB5CSrUOUY0QN13D0DPEQuzANiAlhsxHAYNnSA9ci6gZ5Jq2VRbEVCCzWcRYrl2cKRAgk6gT9NPx9xBvbTqYeofBTyz9IlTaiDm3Zp6RckoxAePIScxzGKBEPTnVK5DRDMS+vtZyLMguBbTZv1BxpAw0UpbJDMQ7LAWCXIMl52EsGVAtKCmvv2YgQNYxY0PTbs97xh9SN07KemfITFHHw4nzntA3jO/kPfsZVSHSqhBpVYi0KkRaFSKtCpHWO4B8e/y4rC/f7/5/D8shXz4u7fHuCO0WQz4u7+4YLYY8LnZ8uTtCu+VrZOm59XiMNbJ7x1etM6tCpFUh0qoQac1A7s325L+P2t1/AoBhu90O04SWQ4b1ANsTP10Pa7XOD5/u4fMWbte74WlYDnlOAM3Jn5KBIesBHp7gXmXS5+WQz5/EQOz42TQVIgWyHgDyqfVGyPD0AOkZThxD+JrznBf7NpO+LodAszZbOHEF8nW9ZYEyZvnll9vtQErDNKH3/ZP9vKoQaVWItCpEWhUirQqRVoVIq0KkVSHSqhBpVYi0LgiygovodvXhAv71NIC/Xl2txPzpxNsbMuPm6vr23Ft9uLr5AdpbpSAPQyy7AAAAAElFTkSuQmCC", + "image": "tb-image:cHJvZ3Jlc3NfYmFyX3N5c3RlbV93aWRnZXRfaW1hZ2UucG5n:IlByb2dyZXNzIGJhciIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80Njg1XzQwOTg5KSI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZF80Njg1XzQwOTg5KSI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyIvPgo8ZyBmaWx0ZXI9InVybCgjZmlsdGVyMV9iXzQ2ODVfNDA5ODkpIj4KPHJlY3QgeD0iMTIiIHk9IjEyIiB3aWR0aD0iMTc2IiBoZWlnaHQ9IjEzNiIgcng9IjQiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9nPgo8cGF0aCBkPSJNMTYuNDQ5NyAxOS41NTMySDE0LjA0MzlWMTguMjlIMTYuNDQ5N0MxNi44Njg3IDE4LjI5IDE3LjIwNzIgMTguMjIyMyAxNy40NjUzIDE4LjA4NjlDMTcuNzIzNSAxNy45NTE1IDE3LjkxMTggMTcuNzY1MyAxOC4wMzAzIDE3LjUyODNDMTguMTUzIDE3LjI4NzEgMTguMjE0NCAxNy4wMTIgMTguMjE0NCAxNi43MDMxQzE4LjIxNDQgMTYuNDExMSAxOC4xNTMgMTYuMTM4MiAxOC4wMzAzIDE1Ljg4NDNDMTcuOTExOCAxNS42MjYxIDE3LjcyMzUgMTUuNDE4OCAxNy40NjUzIDE1LjI2MjJDMTcuMjA3MiAxNS4xMDU2IDE2Ljg2ODcgMTUuMDI3MyAxNi40NDk3IDE1LjAyNzNIMTQuNTMyN1YyM0gxMi45Mzk1VjEzLjc1NzhIMTYuNDQ5N0MxNy4xNjQ5IDEzLjc1NzggMTcuNzcyMSAxMy44ODQ4IDE4LjI3MTUgMTQuMTM4N0MxOC43NzUxIDE0LjM4ODMgMTkuMTU4IDE0LjczNTQgMTkuNDIwNCAxNS4xNzk3QzE5LjY4MjggMTUuNjE5OCAxOS44MTQgMTYuMTIzNCAxOS44MTQgMTYuNjkwNEMxOS44MTQgMTcuMjg3MSAxOS42ODI4IDE3Ljc5OTIgMTkuNDIwNCAxOC4yMjY2QzE5LjE1OCAxOC42NTQgMTguNzc1MSAxOC45ODE5IDE4LjI3MTUgMTkuMjEwNEMxNy43NzIxIDE5LjQzOSAxNy4xNjQ5IDE5LjU1MzIgMTYuNDQ5NyAxOS41NTMyWk0yMi44NzYgMTcuNDM5NVYyM0gyMS4zNDYyVjE2LjEzMThIMjIuODA2MkwyMi44NzYgMTcuNDM5NVpNMjQuOTc3MSAxNi4wODc0TDI0Ljk2NDQgMTcuNTA5M0MyNC44NzEzIDE3LjQ5MjQgMjQuNzY5NyAxNy40Nzk3IDI0LjY1OTcgMTcuNDcxMkMyNC41NTM5IDE3LjQ2MjcgMjQuNDQ4MSAxNy40NTg1IDI0LjM0MjMgMTcuNDU4NUMyNC4wNzk5IDE3LjQ1ODUgMjMuODQ5MyAxNy40OTY2IDIzLjY1MDQgMTcuNTcyOEMyMy40NTE1IDE3LjY0NDcgMjMuMjg0MyAxNy43NTA1IDIzLjE0ODkgMTcuODkwMUMyMy4wMTc3IDE4LjAyNTYgMjIuOTE2MiAxOC4xOTA2IDIyLjg0NDIgMTguMzg1M0MyMi43NzIzIDE4LjU3OTkgMjIuNzMgMTguNzk3OSAyMi43MTczIDE5LjAzOTFMMjIuMzY4MiAxOS4wNjQ1QzIyLjM2ODIgMTguNjMyOCAyMi40MTA1IDE4LjIzMjkgMjIuNDk1MSAxNy44NjQ3QzIyLjU3OTggMTcuNDk2NiAyMi43MDY3IDE3LjE3MjkgMjIuODc2IDE2Ljg5MzZDMjMuMDQ5NSAxNi42MTQzIDIzLjI2NTMgMTYuMzk2MyAyMy41MjM0IDE2LjIzOTdDMjMuNzg1OCAxNi4wODMyIDI0LjA4ODQgMTYuMDA0OSAyNC40MzEyIDE2LjAwNDlDMjQuNTI0MyAxNi4wMDQ5IDI0LjYyMzcgMTYuMDEzMyAyNC43Mjk1IDE2LjAzMDNDMjQuODM5NSAxNi4wNDcyIDI0LjkyMiAxNi4wNjYyIDI0Ljk3NzEgMTYuMDg3NFpNMjUuNzI4NSAxOS42NDIxVjE5LjQ5NjFDMjUuNzI4NSAxOS4wMDEgMjUuODAwNSAxOC41NDE4IDI1Ljk0NDMgMTguMTE4N0MyNi4wODgyIDE3LjY5MTIgMjYuMjk1NiAxNy4zMjEgMjYuNTY2NCAxNy4wMDc4QzI2Ljg0MTUgMTYuNjkwNCAyNy4xNzU4IDE2LjQ0NSAyNy41NjkzIDE2LjI3MTVDMjcuOTY3MSAxNi4wOTM4IDI4LjQxNTcgMTYuMDA0OSAyOC45MTUgMTYuMDA0OUMyOS40MTg2IDE2LjAwNDkgMjkuODY3MiAxNi4wOTM4IDMwLjI2MDcgMTYuMjcxNUMzMC42NTg1IDE2LjQ0NSAzMC45OTUgMTYuNjkwNCAzMS4yNyAxNy4wMDc4QzMxLjU0NTEgMTcuMzIxIDMxLjc1NDYgMTcuNjkxMiAzMS44OTg0IDE4LjExODdDMzIuMDQyMyAxOC41NDE4IDMyLjExNDMgMTkuMDAxIDMyLjExNDMgMTkuNDk2MVYxOS42NDIxQzMyLjExNDMgMjAuMTM3MiAzMi4wNDIzIDIwLjU5NjQgMzEuODk4NCAyMS4wMTk1QzMxLjc1NDYgMjEuNDQyNyAzMS41NDUxIDIxLjgxMyAzMS4yNyAyMi4xMzA0QzMwLjk5NSAyMi40NDM1IDMwLjY2MDYgMjIuNjg5IDMwLjI2NzEgMjIuODY2N0MyOS44NzM1IDIzLjA0MDIgMjkuNDI3MSAyMy4xMjcgMjguOTI3NyAyMy4xMjdDMjguNDI0MiAyMy4xMjcgMjcuOTczNSAyMy4wNDAyIDI3LjU3NTcgMjIuODY2N0MyNy4xODIxIDIyLjY4OSAyNi44NDc4IDIyLjQ0MzUgMjYuNTcyOCAyMi4xMzA0QzI2LjI5NzcgMjEuODEzIDI2LjA4ODIgMjEuNDQyNyAyNS45NDQzIDIxLjAxOTVDMjUuODAwNSAyMC41OTY0IDI1LjcyODUgMjAuMTM3MiAyNS43Mjg1IDE5LjY0MjFaTTI3LjI1ODMgMTkuNDk2MVYxOS42NDIxQzI3LjI1ODMgMTkuOTUxIDI3LjI5IDIwLjI0MyAyNy4zNTM1IDIwLjUxODFDMjcuNDE3IDIwLjc5MzEgMjcuNTE2NCAyMS4wMzQzIDI3LjY1MTkgMjEuMjQxN0MyNy43ODczIDIxLjQ0OTEgMjcuOTYwOCAyMS42MTIgMjguMTcyNCAyMS43MzA1QzI4LjM4NCAyMS44NDkgMjguNjM1NyAyMS45MDgyIDI4LjkyNzcgMjEuOTA4MkMyOS4yMTEzIDIxLjkwODIgMjkuNDU2NyAyMS44NDkgMjkuNjY0MSAyMS43MzA1QzI5Ljg3NTcgMjEuNjEyIDMwLjA0OTIgMjEuNDQ5MSAzMC4xODQ2IDIxLjI0MTdDMzAuMzIgMjEuMDM0MyAzMC40MTk0IDIwLjc5MzEgMzAuNDgyOSAyMC41MTgxQzMwLjU1MDYgMjAuMjQzIDMwLjU4NDUgMTkuOTUxIDMwLjU4NDUgMTkuNjQyMVYxOS40OTYxQzMwLjU4NDUgMTkuMTkxNCAzMC41NTA2IDE4LjkwMzYgMzAuNDgyOSAxOC42MzI4QzMwLjQxOTQgMTguMzU3NyAzMC4zMTc5IDE4LjExNDQgMzAuMTc4MiAxNy45MDI4QzMwLjA0MjggMTcuNjkxMiAyOS44NjkzIDE3LjUyNjIgMjkuNjU3NyAxNy40MDc3QzI5LjQ1MDQgMTcuMjg1IDI5LjIwMjggMTcuMjIzNiAyOC45MTUgMTcuMjIzNkMyOC42MjczIDE3LjIyMzYgMjguMzc3NiAxNy4yODUgMjguMTY2IDE3LjQwNzdDMjcuOTU4NyAxNy41MjYyIDI3Ljc4NzMgMTcuNjkxMiAyNy42NTE5IDE3LjkwMjhDMjcuNTE2NCAxOC4xMTQ0IDI3LjQxNyAxOC4zNTc3IDI3LjM1MzUgMTguNjMyOEMyNy4yOSAxOC45MDM2IDI3LjI1ODMgMTkuMTkxNCAyNy4yNTgzIDE5LjQ5NjFaTTM4LjA0NTQgMTYuMTMxOEgzOS40MzU1VjIyLjgwOTZDMzkuNDM1NSAyMy40Mjc0IDM5LjMwNDQgMjMuOTUyMSAzOS4wNDIgMjQuMzgzOEMzOC43Nzk2IDI0LjgxNTQgMzguNDEzNiAyNS4xNDM0IDM3Ljk0MzggMjUuMzY3N0MzNy40NzQxIDI1LjU5NjIgMzYuOTMwMyAyNS43MTA0IDM2LjMxMjUgMjUuNzEwNEMzNi4wNTAxIDI1LjcxMDQgMzUuNzU4MSAyNS42NzI0IDM1LjQzNjUgMjUuNTk2MkMzNS4xMTkxIDI1LjUyIDM0LjgxMDIgMjUuMzk3MyAzNC41MDk4IDI1LjIyOEMzNC4yMTM1IDI1LjA2MyAzMy45NjYgMjQuODQ1MSAzMy43NjcxIDI0LjU3NDJMMzQuNDg0NCAyMy42NzI5QzM0LjcyOTggMjMuOTY0OCAzNS4wMDA3IDI0LjE3ODUgMzUuMjk2OSAyNC4zMTRDMzUuNTkzMSAyNC40NDk0IDM1LjkwNDEgMjQuNTE3MSAzNi4yMyAyNC41MTcxQzM2LjU4MTIgMjQuNTE3MSAzNi44Nzk2IDI0LjQ1MTUgMzcuMTI1IDI0LjMyMDNDMzcuMzc0NyAyNC4xOTM0IDM3LjU2NzIgMjQuMDA1IDM3LjcwMjYgMjMuNzU1NEMzNy44MzgxIDIzLjUwNTcgMzcuOTA1OCAyMy4yMDEgMzcuOTA1OCAyMi44NDEzVjE3LjY4N0wzOC4wNDU0IDE2LjEzMThaTTMzLjM3OTkgMTkuNjQyMVYxOS41MDg4QzMzLjM3OTkgMTguOTg4MyAzMy40NDM0IDE4LjUxNDMgMzMuNTcwMyAxOC4wODY5QzMzLjY5NzMgMTcuNjU1MyAzMy44NzkyIDE3LjI4NSAzNC4xMTYyIDE2Ljk3NjFDMzQuMzUzMiAxNi42NjI5IDM0LjY0MSAxNi40MjM4IDM0Ljk3OTUgMTYuMjU4OEMzNS4zMTggMTYuMDg5NSAzNS43MDEgMTYuMDA0OSAzNi4xMjg0IDE2LjAwNDlDMzYuNTcyOCAxNi4wMDQ5IDM2Ljk1MTUgMTYuMDg1MyAzNy4yNjQ2IDE2LjI0NjFDMzcuNTgyIDE2LjQwNjkgMzcuODQ2NSAxNi42Mzc1IDM4LjA1ODEgMTYuOTM4QzM4LjI2OTcgMTcuMjM0MiAzOC40MzQ3IDE3LjU4OTcgMzguNTUzMiAxOC4wMDQ0QzM4LjY3NTkgMTguNDE0OSAzOC43NjY5IDE4Ljg3MTkgMzguODI2MiAxOS4zNzU1VjE5LjgwMDhDMzguNzcxMiAyMC4yOTE3IDM4LjY3ODEgMjAuNzQwMiAzOC41NDY5IDIxLjE0NjVDMzguNDE1NyAyMS41NTI3IDM4LjI0MjIgMjEuOTA0IDM4LjAyNjQgMjIuMjAwMkMzNy44MTA1IDIyLjQ5NjQgMzcuNTQzOSAyMi43MjQ5IDM3LjIyNjYgMjIuODg1N0MzNi45MTM0IDIzLjA0NjUgMzYuNTQzMSAyMy4xMjcgMzYuMTE1NyAyMy4xMjdDMzUuNjk2OCAyMy4xMjcgMzUuMzE4IDIzLjA0MDIgMzQuOTc5NSAyMi44NjY3QzM0LjY0NTIgMjIuNjkzMiAzNC4zNTc0IDIyLjQ0OTkgMzQuMTE2MiAyMi4xMzY3QzMzLjg3OTIgMjEuODIzNiAzMy42OTczIDIxLjQ1NTQgMzMuNTcwMyAyMS4wMzIyQzMzLjQ0MzQgMjAuNjA0OCAzMy4zNzk5IDIwLjE0MTQgMzMuMzc5OSAxOS42NDIxWk0zNC45MDk3IDE5LjUwODhWMTkuNjQyMUMzNC45MDk3IDE5Ljk1NTIgMzQuOTM5MyAyMC4yNDcyIDM0Ljk5ODUgMjAuNTE4MUMzNS4wNjIgMjAuNzg4OSAzNS4xNTcyIDIxLjAyOCAzNS4yODQyIDIxLjIzNTRDMzUuNDE1NCAyMS40Mzg1IDM1LjU4MDQgMjEuNTk5MyAzNS43NzkzIDIxLjcxNzhDMzUuOTgyNCAyMS44MzIgMzYuMjIxNSAyMS44ODkyIDM2LjQ5NjYgMjEuODg5MkMzNi44NTYzIDIxLjg4OTIgMzcuMTUwNCAyMS44MTMgMzcuMzc4OSAyMS42NjA2QzM3LjYxMTcgMjEuNTA4MyAzNy43ODk0IDIxLjMwMzEgMzcuOTEyMSAyMS4wNDQ5QzM4LjAzOTEgMjAuNzgyNiAzOC4xMjc5IDIwLjQ5MDYgMzguMTc4NyAyMC4xNjg5VjE5LjAyQzM4LjE1MzMgMTguNzcwMyAzOC4xMDA0IDE4LjUzNzYgMzguMDIgMTguMzIxOEMzNy45NDM4IDE4LjEwNiAzNy44NDAyIDE3LjkxNzYgMzcuNzA5IDE3Ljc1NjhDMzcuNTc3OCAxNy41OTE4IDM3LjQxMjggMTcuNDY0OCAzNy4yMTM5IDE3LjM3NkMzNy4wMTUgMTcuMjgyOSAzNi43ODAxIDE3LjIzNjMgMzYuNTA5MyAxNy4yMzYzQzM2LjIzNDIgMTcuMjM2MyAzNS45OTUxIDE3LjI5NTYgMzUuNzkyIDE3LjQxNDFDMzUuNTg4OSAxNy41MzI2IDM1LjQyMTcgMTcuNjk1NSAzNS4yOTA1IDE3LjkwMjhDMzUuMTYzNiAxOC4xMTAyIDM1LjA2ODQgMTguMzUxNCAzNS4wMDQ5IDE4LjYyNjVDMzQuOTQxNCAxOC45MDE1IDM0LjkwOTcgMTkuMTk1NiAzNC45MDk3IDE5LjUwODhaTTQyLjgwODYgMTcuNDM5NVYyM0g0MS4yNzg4VjE2LjEzMThINDIuNzM4OEw0Mi44MDg2IDE3LjQzOTVaTTQ0LjkwOTcgMTYuMDg3NEw0NC44OTcgMTcuNTA5M0M0NC44MDM5IDE3LjQ5MjQgNDQuNzAyMyAxNy40Nzk3IDQ0LjU5MjMgMTcuNDcxMkM0NC40ODY1IDE3LjQ2MjcgNDQuMzgwNyAxNy40NTg1IDQ0LjI3NDkgMTcuNDU4NUM0NC4wMTI1IDE3LjQ1ODUgNDMuNzgxOSAxNy40OTY2IDQzLjU4MyAxNy41NzI4QzQzLjM4NDEgMTcuNjQ0NyA0My4yMTcgMTcuNzUwNSA0My4wODE1IDE3Ljg5MDFDNDIuOTUwNCAxOC4wMjU2IDQyLjg0ODggMTguMTkwNiA0Mi43NzY5IDE4LjM4NTNDNDIuNzA0OSAxOC41Nzk5IDQyLjY2MjYgMTguNzk3OSA0Mi42NDk5IDE5LjAzOTFMNDIuMzAwOCAxOS4wNjQ1QzQyLjMwMDggMTguNjMyOCA0Mi4zNDMxIDE4LjIzMjkgNDIuNDI3NyAxNy44NjQ3QzQyLjUxMjQgMTcuNDk2NiA0Mi42MzkzIDE3LjE3MjkgNDIuODA4NiAxNi44OTM2QzQyLjk4MjEgMTYuNjE0MyA0My4xOTc5IDE2LjM5NjMgNDMuNDU2MSAxNi4yMzk3QzQzLjcxODQgMTYuMDgzMiA0NC4wMjEgMTYuMDA0OSA0NC4zNjM4IDE2LjAwNDlDNDQuNDU2OSAxNi4wMDQ5IDQ0LjU1NjMgMTYuMDEzMyA0NC42NjIxIDE2LjAzMDNDNDQuNzcyMSAxNi4wNDcyIDQ0Ljg1NDcgMTYuMDY2MiA0NC45MDk3IDE2LjA4NzRaTTQ5LjAxOSAyMy4xMjdDNDguNTExMiAyMy4xMjcgNDguMDUyMSAyMy4wNDQ0IDQ3LjY0MTYgMjIuODc5NEM0Ny4yMzU0IDIyLjcxMDEgNDYuODg4MyAyMi40NzUzIDQ2LjYwMDYgMjIuMTc0OEM0Ni4zMTcxIDIxLjg3NDMgNDYuMDk5MSAyMS41MjEgNDUuOTQ2OCAyMS4xMTQ3QzQ1Ljc5NDQgMjAuNzA4NSA0NS43MTgzIDIwLjI3MDUgNDUuNzE4MyAxOS44MDA4VjE5LjU0NjlDNDUuNzE4MyAxOS4wMDk0IDQ1Ljc5NjUgMTguNTIyOCA0NS45NTMxIDE4LjA4NjlDNDYuMTA5NyAxNy42NTEgNDYuMzI3NiAxNy4yNzg2IDQ2LjYwNjkgMTYuOTY5N0M0Ni44ODYyIDE2LjY1NjYgNDcuMjE2MyAxNi40MTc1IDQ3LjU5NzIgMTYuMjUyNEM0Ny45NzggMTYuMDg3NCA0OC4zOTA2IDE2LjAwNDkgNDguODM1IDE2LjAwNDlDNDkuMzI1OCAxNi4wMDQ5IDQ5Ljc1NTQgMTYuMDg3NCA1MC4xMjM1IDE2LjI1MjRDNTAuNDkxNyAxNi40MTc1IDUwLjc5NjQgMTYuNjUwMiA1MS4wMzc2IDE2Ljk1MDdDNTEuMjgzIDE3LjI0NjkgNTEuNDY1IDE3LjYwMDMgNTEuNTgzNSAxOC4wMTA3QzUxLjcwNjIgMTguNDIxMiA1MS43Njc2IDE4Ljg3NCA1MS43Njc2IDE5LjM2OTFWMjAuMDIyOUg0Ni40NjA5VjE4LjkyNDhINTAuMjU2OFYxOC44MDQyQzUwLjI0ODQgMTguNTI5MSA1MC4xOTM0IDE4LjI3MSA1MC4wOTE4IDE4LjAyOThDNDkuOTk0NSAxNy43ODg2IDQ5Ljg0NDIgMTcuNTkzOSA0OS42NDExIDE3LjQ0NThDNDkuNDM4IDE3LjI5NzcgNDkuMTY3MiAxNy4yMjM2IDQ4LjgyODYgMTcuMjIzNkM0OC41NzQ3IDE3LjIyMzYgNDguMzQ4MyAxNy4yNzg2IDQ4LjE0OTQgMTcuMzg4N0M0Ny45NTQ4IDE3LjQ5NDUgNDcuNzkxOCAxNy42NDg5IDQ3LjY2MDYgMTcuODUyMUM0Ny41Mjk1IDE4LjA1NTIgNDcuNDI3OSAxOC4zMDA2IDQ3LjM1NiAxOC41ODg0QzQ3LjI4ODIgMTguODcxOSA0Ny4yNTQ0IDE5LjE5MTQgNDcuMjU0NCAxOS41NDY5VjE5LjgwMDhDNDcuMjU0NCAyMC4xMDEyIDQ3LjI5NDYgMjAuMzgwNSA0Ny4zNzUgMjAuNjM4N0M0Ny40NTk2IDIwLjg5MjYgNDcuNTgyNCAyMS4xMTQ3IDQ3Ljc0MzIgMjEuMzA1MkM0Ny45MDQgMjEuNDk1NiA0OC4wOTg2IDIxLjY0NTggNDguMzI3MSAyMS43NTU5QzQ4LjU1NTcgMjEuODYxNyA0OC44MTU5IDIxLjkxNDYgNDkuMTA3OSAyMS45MTQ2QzQ5LjQ3NjEgMjEuOTE0NiA0OS44MDQgMjEuODQwNSA1MC4wOTE4IDIxLjY5MjRDNTAuMzc5NiAyMS41NDQzIDUwLjYyOTIgMjEuMzM0OCA1MC44NDA4IDIxLjA2NEw1MS42NDcgMjEuODQ0N0M1MS40OTg5IDIyLjA2MDUgNTEuMzA2MyAyMi4yNjc5IDUxLjA2OTMgMjIuNDY2OEM1MC44MzI0IDIyLjY2MTUgNTAuNTQyNSAyMi44MjAxIDUwLjE5OTcgMjIuOTQyOUM0OS44NjEyIDIzLjA2NTYgNDkuNDY3NiAyMy4xMjcgNDkuMDE5IDIzLjEyN1pNNTcuMDY0IDIxLjE0MDFDNTcuMDY0IDIwLjk4NzggNTcuMDI1OSAyMC44NTAzIDU2Ljk0OTcgMjAuNzI3NUM1Ni44NzM1IDIwLjYwMDYgNTYuNzI3NSAyMC40ODYzIDU2LjUxMTcgMjAuMzg0OEM1Ni4zMDAxIDIwLjI4MzIgNTUuOTg3IDIwLjE5MDEgNTUuNTcyMyAyMC4xMDU1QzU1LjIwODMgMjAuMDI1MSA1NC44NzQgMTkuOTI5OSA1NC41NjkzIDE5LjgxOThDNTQuMjY4OSAxOS43MDU2IDU0LjAxMDcgMTkuNTY4IDUzLjc5NDkgMTkuNDA3MkM1My41NzkxIDE5LjI0NjQgNTMuNDExOSAxOS4wNTYgNTMuMjkzNSAxOC44MzU5QzUzLjE3NSAxOC42MTU5IDUzLjExNTcgMTguMzYyIDUzLjExNTcgMTguMDc0MkM1My4xMTU3IDE3Ljc5NDkgNTMuMTc3MSAxNy41MzA0IDUzLjI5OTggMTcuMjgwOEM1My40MjI1IDE3LjAzMTEgNTMuNTk4MSAxNi44MTEgNTMuODI2NyAxNi42MjA2QzU0LjA1NTIgMTYuNDMwMiA1NC4zMzI0IDE2LjI3OTkgNTQuNjU4MiAxNi4xNjk5QzU0Ljk4ODMgMTYuMDU5OSA1NS4zNTY0IDE2LjAwNDkgNTUuNzYyNyAxNi4wMDQ5QzU2LjMzODIgMTYuMDA0OSA1Ni44MzEyIDE2LjEwMjIgNTcuMjQxNyAxNi4yOTY5QzU3LjY1NjQgMTYuNDg3MyA1Ny45NzM4IDE2Ljc0NzYgNTguMTkzOCAxNy4wNzc2QzU4LjQxMzkgMTcuNDAzNSA1OC41MjM5IDE3Ljc3MTYgNTguNTIzOSAxOC4xODIxSDU2Ljk5NDFDNTYuOTk0MSAxOC4wMDAyIDU2Ljk0NzYgMTcuODMwOSA1Ni44NTQ1IDE3LjY3NDNDNTYuNzY1NiAxNy41MTM1IDU2LjYzMDIgMTcuMzg0NCA1Ni40NDgyIDE3LjI4NzFDNTYuMjY2MyAxNy4xODU1IDU2LjAzNzggMTcuMTM0OCA1NS43NjI3IDE3LjEzNDhDNTUuNTAwMyAxNy4xMzQ4IDU1LjI4MjQgMTcuMTc3MSA1NS4xMDg5IDE3LjI2MTdDNTQuOTM5NiAxNy4zNDIxIDU0LjgxMjcgMTcuNDQ3OSA1NC43MjggMTcuNTc5MUM1NC42NDc2IDE3LjcxMDMgNTQuNjA3NCAxNy44NTQyIDU0LjYwNzQgMTguMDEwN0M1NC42MDc0IDE4LjEyNSA1NC42Mjg2IDE4LjIyODcgNTQuNjcwOSAxOC4zMjE4QzU0LjcxNzQgMTguNDEwNiA1NC43OTM2IDE4LjQ5MzIgNTQuODk5NCAxOC41NjkzQzU1LjAwNTIgMTguNjQxMyA1NS4xNDkxIDE4LjcwOSA1NS4zMzExIDE4Ljc3MjVDNTUuNTE3MyAxOC44MzU5IDU1Ljc1IDE4Ljg5NzMgNTYuMDI5MyAxOC45NTY1QzU2LjU1NCAxOS4wNjY2IDU3LjAwNDcgMTkuMjA4MyA1Ny4zODEzIDE5LjM4MThDNTcuNzYyMiAxOS41NTExIDU4LjA1NDIgMTkuNzcxMiA1OC4yNTczIDIwLjA0MkM1OC40NjA0IDIwLjMwODYgNTguNTYyIDIwLjY0NzEgNTguNTYyIDIxLjA1NzZDNTguNTYyIDIxLjM2MjMgNTguNDk2NCAyMS42NDE2IDU4LjM2NTIgMjEuODk1NUM1OC4yMzgzIDIyLjE0NTIgNTguMDUyMSAyMi4zNjMxIDU3LjgwNjYgMjIuNTQ5M0M1Ny41NjEyIDIyLjczMTMgNTcuMjY3MSAyMi44NzMgNTYuOTI0MyAyMi45NzQ2QzU2LjU4NTggMjMuMDc2MiA1Ni4yMDQ5IDIzLjEyNyA1NS43ODE3IDIzLjEyN0M1NS4xNTk3IDIzLjEyNyA1NC42MzI4IDIzLjAxNjkgNTQuMjAxMiAyMi43OTY5QzUzLjc2OTUgMjIuNTcyNiA1My40NDE2IDIyLjI4NjkgNTMuMjE3MyAyMS45Mzk5QzUyLjk5NzIgMjEuNTg4NyA1Mi44ODcyIDIxLjIyNDggNTIuODg3MiAyMC44NDgxSDU0LjM2NjJDNTQuMzgzMSAyMS4xMzE3IDU0LjQ2MTQgMjEuMzU4MSA1NC42MDExIDIxLjUyNzNDNTQuNzQ1IDIxLjY5MjQgNTQuOTIyNyAyMS44MTMgNTUuMTM0MyAyMS44ODkyQzU1LjM1MDEgMjEuOTYxMSA1NS41NzIzIDIxLjk5NzEgNTUuODAwOCAyMS45OTcxQzU2LjA3NTggMjEuOTk3MSA1Ni4zMDY1IDIxLjk2MTEgNTYuNDkyNyAyMS44ODkyQzU2LjY3ODkgMjEuODEzIDU2LjgyMDYgMjEuNzExNCA1Ni45MTggMjEuNTg0NUM1Ny4wMTUzIDIxLjQ1MzMgNTcuMDY0IDIxLjMwNTIgNTcuMDY0IDIxLjE0MDFaTTY0LjAwNDQgMjEuMTQwMUM2NC4wMDQ0IDIwLjk4NzggNjMuOTY2MyAyMC44NTAzIDYzLjg5MDEgMjAuNzI3NUM2My44MTQgMjAuNjAwNiA2My42NjggMjAuNDg2MyA2My40NTIxIDIwLjM4NDhDNjMuMjQwNiAyMC4yODMyIDYyLjkyNzQgMjAuMTkwMSA2Mi41MTI3IDIwLjEwNTVDNjIuMTQ4OCAyMC4wMjUxIDYxLjgxNDUgMTkuOTI5OSA2MS41MDk4IDE5LjgxOThDNjEuMjA5MyAxOS43MDU2IDYwLjk1MTIgMTkuNTY4IDYwLjczNTQgMTkuNDA3MkM2MC41MTk1IDE5LjI0NjQgNjAuMzUyNCAxOS4wNTYgNjAuMjMzOSAxOC44MzU5QzYwLjExNTQgMTguNjE1OSA2MC4wNTYyIDE4LjM2MiA2MC4wNTYyIDE4LjA3NDJDNjAuMDU2MiAxNy43OTQ5IDYwLjExNzUgMTcuNTMwNCA2MC4yNDAyIDE3LjI4MDhDNjAuMzYzIDE3LjAzMTEgNjAuNTM4NiAxNi44MTEgNjAuNzY3MSAxNi42MjA2QzYwLjk5NTYgMTYuNDMwMiA2MS4yNzI4IDE2LjI3OTkgNjEuNTk4NiAxNi4xNjk5QzYxLjkyODcgMTYuMDU5OSA2Mi4yOTY5IDE2LjAwNDkgNjIuNzAzMSAxNi4wMDQ5QzYzLjI3ODYgMTYuMDA0OSA2My43NzE2IDE2LjEwMjIgNjQuMTgyMSAxNi4yOTY5QzY0LjU5NjggMTYuNDg3MyA2NC45MTQyIDE2Ljc0NzYgNjUuMTM0MyAxNy4wNzc2QzY1LjM1NDMgMTcuNDAzNSA2NS40NjQ0IDE3Ljc3MTYgNjUuNDY0NCAxOC4xODIxSDYzLjkzNDZDNjMuOTM0NiAxOC4wMDAyIDYzLjg4OCAxNy44MzA5IDYzLjc5NDkgMTcuNjc0M0M2My43MDYxIDE3LjUxMzUgNjMuNTcwNiAxNy4zODQ0IDYzLjM4ODcgMTcuMjg3MUM2My4yMDY3IDE3LjE4NTUgNjIuOTc4MiAxNy4xMzQ4IDYyLjcwMzEgMTcuMTM0OEM2Mi40NDA4IDE3LjEzNDggNjIuMjIyOCAxNy4xNzcxIDYyLjA0OTMgMTcuMjYxN0M2MS44OCAxNy4zNDIxIDYxLjc1MzEgMTcuNDQ3OSA2MS42Njg1IDE3LjU3OTFDNjEuNTg4MSAxNy43MTAzIDYxLjU0NzkgMTcuODU0MiA2MS41NDc5IDE4LjAxMDdDNjEuNTQ3OSAxOC4xMjUgNjEuNTY5IDE4LjIyODcgNjEuNjExMyAxOC4zMjE4QzYxLjY1NzkgMTguNDEwNiA2MS43MzQgMTguNDkzMiA2MS44Mzk4IDE4LjU2OTNDNjEuOTQ1NiAxOC42NDEzIDYyLjA4OTUgMTguNzA5IDYyLjI3MTUgMTguNzcyNUM2Mi40NTc3IDE4LjgzNTkgNjIuNjkwNCAxOC44OTczIDYyLjk2OTcgMTguOTU2NUM2My40OTQ1IDE5LjA2NjYgNjMuOTQ1MSAxOS4yMDgzIDY0LjMyMTggMTkuMzgxOEM2NC43MDI2IDE5LjU1MTEgNjQuOTk0NiAxOS43NzEyIDY1LjE5NzggMjAuMDQyQzY1LjQwMDkgMjAuMzA4NiA2NS41MDI0IDIwLjY0NzEgNjUuNTAyNCAyMS4wNTc2QzY1LjUwMjQgMjEuMzYyMyA2NS40MzY4IDIxLjY0MTYgNjUuMzA1NyAyMS44OTU1QzY1LjE3ODcgMjIuMTQ1MiA2NC45OTI1IDIyLjM2MzEgNjQuNzQ3MSAyMi41NDkzQzY0LjUwMTYgMjIuNzMxMyA2NC4yMDc1IDIyLjg3MyA2My44NjQ3IDIyLjk3NDZDNjMuNTI2MiAyMy4wNzYyIDYzLjE0NTMgMjMuMTI3IDYyLjcyMjIgMjMuMTI3QzYyLjEwMDEgMjMuMTI3IDYxLjU3MzIgMjMuMDE2OSA2MS4xNDE2IDIyLjc5NjlDNjAuNzEgMjIuNTcyNiA2MC4zODIgMjIuMjg2OSA2MC4xNTc3IDIxLjkzOTlDNTkuOTM3NyAyMS41ODg3IDU5LjgyNzYgMjEuMjI0OCA1OS44Mjc2IDIwLjg0ODFINjEuMzA2NkM2MS4zMjM2IDIxLjEzMTcgNjEuNDAxOSAyMS4zNTgxIDYxLjU0MTUgMjEuNTI3M0M2MS42ODU0IDIxLjY5MjQgNjEuODYzMSAyMS44MTMgNjIuMDc0NyAyMS44ODkyQzYyLjI5MDUgMjEuOTYxMSA2Mi41MTI3IDIxLjk5NzEgNjIuNzQxMiAyMS45OTcxQzYzLjAxNjMgMjEuOTk3MSA2My4yNDY5IDIxLjk2MTEgNjMuNDMzMSAyMS44ODkyQzYzLjYxOTMgMjEuODEzIDYzLjc2MTEgMjEuNzExNCA2My44NTg0IDIxLjU4NDVDNjMuOTU1NyAyMS40NTMzIDY0LjAwNDQgMjEuMzA1MiA2NC4wMDQ0IDIxLjE0MDFaTTcwLjU4NTQgMTMuMjVINzIuMTE1MlYyMS41MzM3TDcxLjk2OTIgMjNINzAuNTg1NFYxMy4yNVpNNzYuNTk2NyAxOS41MDI0VjE5LjYzNTdDNzYuNTk2NyAyMC4xNDM2IDc2LjUzOTYgMjAuNjExMiA3Ni40MjUzIDIxLjAzODZDNzYuMzE1MyAyMS40NjE4IDc2LjE0NiAyMS44Mjk5IDc1LjkxNzUgMjIuMTQzMUM3NS42OTMyIDIyLjQ1NjIgNzUuNDEzOSAyMi42OTk1IDc1LjA3OTYgMjIuODczQzc0Ljc0OTUgMjMuMDQyMyA3NC4zNjY1IDIzLjEyNyA3My45MzA3IDIzLjEyN0M3My41MDMzIDIzLjEyNyA3My4xMzA5IDIzLjA0NjUgNzIuODEzNSAyMi44ODU3QzcyLjQ5NjEgMjIuNzI0OSA3Mi4yMjk1IDIyLjQ5NjQgNzIuMDEzNyAyMi4yMDAyQzcxLjgwMjEgMjEuOTA0IDcxLjYzMDcgMjEuNTUwNiA3MS40OTk1IDIxLjE0MDFDNzEuMzY4MyAyMC43Mjk3IDcxLjI3NTIgMjAuMjc2OSA3MS4yMjAyIDE5Ljc4MTdWMTkuMzU2NEM3MS4yNzUyIDE4Ljg1NzEgNzEuMzY4MyAxOC40MDQzIDcxLjQ5OTUgMTcuOTk4QzcxLjYzMDcgMTcuNTg3NiA3MS44MDIxIDE3LjIzNDIgNzIuMDEzNyAxNi45MzhDNzIuMjI5NSAxNi42Mzc1IDcyLjQ5NCAxNi40MDY5IDcyLjgwNzEgMTYuMjQ2MUM3My4xMjQ1IDE2LjA4NTMgNzMuNDk0OCAxNi4wMDQ5IDczLjkxOCAxNi4wMDQ5Qzc0LjM1ODEgMTYuMDA0OSA3NC43NDUzIDE2LjA4OTUgNzUuMDc5NiAxNi4yNTg4Qzc1LjQxODEgMTYuNDI4MSA3NS42OTk1IDE2LjY2OTMgNzUuOTIzOCAxNi45ODI0Qzc2LjE0ODEgMTcuMjkxMyA3Ni4zMTUzIDE3LjY1OTUgNzYuNDI1MyAxOC4wODY5Qzc2LjUzOTYgMTguNTE0MyA3Ni41OTY3IDE4Ljk4NjIgNzYuNTk2NyAxOS41MDI0Wk03NS4wNjY5IDE5LjYzNTdWMTkuNTAyNEM3NS4wNjY5IDE5LjE5MzUgNzUuMDQxNSAxOC45MDM2IDc0Ljk5MDcgMTguNjMyOEM3NC45Mzk5IDE4LjM1NzcgNzQuODU1MyAxOC4xMTY1IDc0LjczNjggMTcuOTA5MkM3NC42MjI2IDE3LjcwMTggNzQuNDY2IDE3LjUzODkgNzQuMjY3MSAxNy40MjA0Qzc0LjA3MjQgMTcuMjk3NyA3My44MjkxIDE3LjIzNjMgNzMuNTM3MSAxNy4yMzYzQzczLjI2NjMgMTcuMjM2MyA3My4wMzM1IDE3LjI4MjkgNzIuODM4OSAxNy4zNzZDNzIuNjQ0MiAxNy40NjkxIDcyLjQ4MTMgMTcuNTk2IDcyLjM1MDEgMTcuNzU2OEM3Mi4yMTg5IDE3LjkxNzYgNzIuMTE1MiAxOC4xMDM4IDcyLjAzOTEgMTguMzE1NEM3MS45NjcxIDE4LjUyNyA3MS45MTg1IDE4Ljc1NTUgNzEuODkzMSAxOS4wMDFWMjAuMTQ5OUM3MS45MzEyIDIwLjQ2NzMgNzIuMDExNiAyMC43NTkzIDcyLjEzNDMgMjEuMDI1OUM3Mi4yNjEyIDIxLjI4ODIgNzIuNDM5IDIxLjQ5OTggNzIuNjY3NSAyMS42NjA2QzcyLjg5NiAyMS44MTcyIDczLjE5MDEgMjEuODk1NSA3My41NDk4IDIxLjg5NTVDNzMuODMzMyAyMS44OTU1IDc0LjA3MjQgMjEuODM4NCA3NC4yNjcxIDIxLjcyNDFDNzQuNDYxOCAyMS42MDk5IDc0LjYxNjIgMjEuNDUxMiA3NC43MzA1IDIxLjI0OEM3NC44NDkgMjEuMDQwNyA3NC45MzM2IDIwLjc5OTUgNzQuOTg0NCAyMC41MjQ0Qzc1LjAzOTQgMjAuMjQ5MyA3NS4wNjY5IDE5Ljk1MzEgNzUuMDY2OSAxOS42MzU3Wk04Mi4wMTM3IDIxLjYyMjZWMTguMzQ3MkM4Mi4wMTM3IDE4LjEwMTcgODEuOTY5MiAxNy44OTAxIDgxLjg4MDQgMTcuNzEyNEM4MS43OTE1IDE3LjUzNDcgODEuNjU2MSAxNy4zOTcxIDgxLjQ3NDEgMTcuMjk5OEM4MS4yOTY0IDE3LjIwMjUgODEuMDcyMSAxNy4xNTM4IDgwLjgwMTMgMTcuMTUzOEM4MC41NTE2IDE3LjE1MzggODAuMzM1OCAxNy4xOTYxIDgwLjE1MzggMTcuMjgwOEM3OS45NzE4IDE3LjM2NTQgNzkuODMwMSAxNy40Nzk3IDc5LjcyODUgMTcuNjIzNUM3OS42MjcgMTcuNzY3NCA3OS41NzYyIDE3LjkzMDMgNzkuNTc2MiAxOC4xMTIzSDc4LjA1MjdDNzguMDUyNyAxNy44NDE1IDc4LjExODMgMTcuNTc5MSA3OC4yNDk1IDE3LjMyNTJDNzguMzgwNyAxNy4wNzEzIDc4LjU3MTEgMTYuODQ0OSA3OC44MjA4IDE2LjY0NkM3OS4wNzA1IDE2LjQ0NzEgNzkuMzY4OCAxNi4yOTA1IDc5LjcxNTggMTYuMTc2M0M4MC4wNjI4IDE2LjA2MiA4MC40NTIxIDE2LjAwNDkgODAuODgzOCAxNi4wMDQ5QzgxLjQwMDEgMTYuMDA0OSA4MS44NTcxIDE2LjA5MTYgODIuMjU0OSAxNi4yNjUxQzgyLjY1NjkgMTYuNDM4NiA4Mi45NzIyIDE2LjcwMSA4My4yMDA3IDE3LjA1MjJDODMuNDMzNCAxNy4zOTkzIDgzLjU0OTggMTcuODM1MSA4My41NDk4IDE4LjM1OTlWMjEuNDEzMUM4My41NDk4IDIxLjcyNjIgODMuNTcxIDIyLjAwNzYgODMuNjEzMyAyMi4yNTczQzgzLjY1OTggMjIuNTAyOCA4My43MjU0IDIyLjcxNjUgODMuODEwMSAyMi44OTg0VjIzSDgyLjI0MjJDODIuMTcwMiAyMi44MzUgODIuMTEzMSAyMi42MjU1IDgyLjA3MDggMjIuMzcxNkM4Mi4wMzI3IDIyLjExMzQgODIuMDEzNyAyMS44NjM4IDgyLjAxMzcgMjEuNjIyNlpNODIuMjM1OCAxOC44MjMyTDgyLjI0ODUgMTkuNzY5SDgxLjE1MDRDODAuODY2OSAxOS43NjkgODAuNjE3MiAxOS43OTY1IDgwLjQwMTQgMTkuODUxNkM4MC4xODU1IDE5LjkwMjMgODAuMDA1NyAxOS45Nzg1IDc5Ljg2MTggMjAuMDgwMUM3OS43MTc5IDIwLjE4MTYgNzkuNjEgMjAuMzA0NCA3OS41MzgxIDIwLjQ0ODJDNzkuNDY2MSAyMC41OTIxIDc5LjQzMDIgMjAuNzU1IDc5LjQzMDIgMjAuOTM3Qzc5LjQzMDIgMjEuMTE5IDc5LjQ3MjUgMjEuMjg2MSA3OS41NTcxIDIxLjQzODVDNzkuNjQxOCAyMS41ODY2IDc5Ljc2NDUgMjEuNzAzIDc5LjkyNTMgMjEuNzg3NkM4MC4wOTAzIDIxLjg3MjIgODAuMjg5MiAyMS45MTQ2IDgwLjUyMiAyMS45MTQ2QzgwLjgzNTEgMjEuOTE0NiA4MS4xMDgxIDIxLjg1MTEgODEuMzQwOCAyMS43MjQxQzgxLjU3NzggMjEuNTkyOSA4MS43NjQgMjEuNDM0MiA4MS44OTk0IDIxLjI0OEM4Mi4wMzQ4IDIxLjA1NzYgODIuMTA2OCAyMC44Nzc4IDgyLjExNTIgMjAuNzA4NUw4Mi42MTA0IDIxLjM4NzdDODIuNTU5NiAyMS41NjEyIDgyLjQ3MjggMjEuNzQ3NCA4Mi4zNTAxIDIxLjk0NjNDODIuMjI3NCAyMi4xNDUyIDgyLjA2NjYgMjIuMzM1NiA4MS44Njc3IDIyLjUxNzZDODEuNjczIDIyLjY5NTMgODEuNDM4MiAyMi44NDEzIDgxLjE2MzEgMjIuOTU1NkM4MC44OTIzIDIzLjA2OTggODAuNTc5MSAyMy4xMjcgODAuMjIzNiAyMy4xMjdDNzkuNzc1MSAyMy4xMjcgNzkuMzc1MiAyMy4wMzgxIDc5LjAyMzkgMjIuODYwNEM3OC42NzI3IDIyLjY3ODQgNzguMzk3NiAyMi40MzUxIDc4LjE5ODcgMjIuMTMwNEM3Ny45OTk4IDIxLjgyMTUgNzcuOTAwNCAyMS40NzIzIDc3LjkwMDQgMjEuMDgzQzc3LjkwMDQgMjAuNzE5MSA3Ny45NjgxIDIwLjM5NzUgNzguMTAzNSAyMC4xMTgyQzc4LjI0MzIgMTkuODM0NiA3OC40NDYzIDE5LjU5NzcgNzguNzEyOSAxOS40MDcyQzc4Ljk4MzcgMTkuMjE2OCA3OS4zMTM4IDE5LjA3MjkgNzkuNzAzMSAxOC45NzU2QzgwLjA5MjQgMTguODc0IDgwLjUzNjggMTguODIzMiA4MS4wMzYxIDE4LjgyMzJIODIuMjM1OFpNODYuOTM1NSAxNy40Mzk1VjIzSDg1LjQwNThWMTYuMTMxOEg4Ni44NjU3TDg2LjkzNTUgMTcuNDM5NVpNODkuMDM2NiAxNi4wODc0TDg5LjAyMzkgMTcuNTA5M0M4OC45MzA4IDE3LjQ5MjQgODguODI5MyAxNy40Nzk3IDg4LjcxOTIgMTcuNDcxMkM4OC42MTM0IDE3LjQ2MjcgODguNTA3NiAxNy40NTg1IDg4LjQwMTkgMTcuNDU4NUM4OC4xMzk1IDE3LjQ1ODUgODcuOTA4OSAxNy40OTY2IDg3LjcxIDE3LjU3MjhDODcuNTExMSAxNy42NDQ3IDg3LjM0MzkgMTcuNzUwNSA4Ny4yMDg1IDE3Ljg5MDFDODcuMDc3MyAxOC4wMjU2IDg2Ljk3NTcgMTguMTkwNiA4Ni45MDM4IDE4LjM4NTNDODYuODMxOSAxOC41Nzk5IDg2Ljc4OTYgMTguNzk3OSA4Ni43NzY5IDE5LjAzOTFMODYuNDI3NyAxOS4wNjQ1Qzg2LjQyNzcgMTguNjMyOCA4Ni40NzAxIDE4LjIzMjkgODYuNTU0NyAxNy44NjQ3Qzg2LjYzOTMgMTcuNDk2NiA4Ni43NjYzIDE3LjE3MjkgODYuOTM1NSAxNi44OTM2Qzg3LjEwOSAxNi42MTQzIDg3LjMyNDkgMTYuMzk2MyA4Ny41ODMgMTYuMjM5N0M4Ny44NDU0IDE2LjA4MzIgODguMTQ3OSAxNi4wMDQ5IDg4LjQ5MDcgMTYuMDA0OUM4OC41ODM4IDE2LjAwNDkgODguNjgzMyAxNi4wMTMzIDg4Ljc4OTEgMTYuMDMwM0M4OC44OTkxIDE2LjA0NzIgODguOTgxNiAxNi4wNjYyIDg5LjAzNjYgMTYuMDg3NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE2LjYwNTUgOTcuMTkxNEgxOC4yOTNDMTguOTQ5MiA5Ny4xOTE0IDE5LjQ5MjIgOTcuMDc4MSAxOS45MjE5IDk2Ljg1MTZDMjAuMzU5NCA5Ni42MjUgMjAuNjgzNiA5Ni4zMTI1IDIwLjg5NDUgOTUuOTE0MUMyMS4xMDU1IDk1LjUxNTYgMjEuMjEwOSA5NS4wNTg2IDIxLjIxMDkgOTQuNTQzQzIxLjIxMDkgOTQuMDAzOSAyMS4xMTMzIDkzLjU0MyAyMC45MTggOTMuMTYwMkMyMC43MzA1IDkyLjc2OTUgMjAuNDQxNCA5Mi40Njg4IDIwLjA1MDggOTIuMjU3OEMxOS42NjggOTIuMDQ2OSAxOS4xNzk3IDkxLjk0MTQgMTguNTg1OSA5MS45NDE0QzE4LjA4NTkgOTEuOTQxNCAxNy42MzI4IDkyLjA0MyAxNy4yMjY2IDkyLjI0NjFDMTYuODI4MSA5Mi40NDE0IDE2LjUxMTcgOTIuNzIyNyAxNi4yNzczIDkzLjA4OThDMTYuMDQzIDkzLjQ0OTIgMTUuOTI1OCA5My44Nzg5IDE1LjkyNTggOTQuMzc4OUgxMy4wODk4QzEzLjA4OTggOTMuNDcyNyAxMy4zMjgxIDkyLjY2OCAxMy44MDQ3IDkxLjk2NDhDMTQuMjgxMiA5MS4yNjE3IDE0LjkyOTcgOTAuNzEwOSAxNS43NSA5MC4zMTI1QzE2LjU3ODEgODkuOTA2MiAxNy41MDc4IDg5LjcwMzEgMTguNTM5MSA4OS43MDMxQzE5LjY0MDYgODkuNzAzMSAyMC42MDE2IDg5Ljg4NjcgMjEuNDIxOSA5MC4yNTM5QzIyLjI1IDkwLjYxMzMgMjIuODk0NSA5MS4xNTIzIDIzLjM1NTUgOTEuODcxMUMyMy44MTY0IDkyLjU4OTggMjQuMDQ2OSA5My40ODA1IDI0LjA0NjkgOTQuNTQzQzI0LjA0NjkgOTUuMDI3MyAyMy45MzM2IDk1LjUxOTUgMjMuNzA3IDk2LjAxOTVDMjMuNDgwNSA5Ni41MTk1IDIzLjE0NDUgOTYuOTc2NiAyMi42OTkyIDk3LjM5MDZDMjIuMjUzOSA5Ny43OTY5IDIxLjY5OTIgOTguMTI4OSAyMS4wMzUyIDk4LjM4NjdDMjAuMzcxMSA5OC42MzY3IDE5LjYwMTYgOTguNzYxNyAxOC43MjY2IDk4Ljc2MTdIMTYuNjA1NVY5Ny4xOTE0Wk0xNi42MDU1IDk5LjM5NDVWOTcuODQ3N0gxOC43MjY2QzE5LjcyNjYgOTcuODQ3NyAyMC41NzgxIDk3Ljk2NDggMjEuMjgxMiA5OC4xOTkyQzIxLjk5MjIgOTguNDMzNiAyMi41NzAzIDk4Ljc1NzggMjMuMDE1NiA5OS4xNzE5QzIzLjQ2MDkgOTkuNTc4MSAyMy43ODUyIDEwMC4wNDMgMjMuOTg4MyAxMDAuNTY2QzI0LjE5OTIgMTAxLjA5IDI0LjMwNDcgMTAxLjY0NSAyNC4zMDQ3IDEwMi4yM0MyNC4zMDQ3IDEwMy4wMjcgMjQuMTYwMiAxMDMuNzM4IDIzLjg3MTEgMTA0LjM2M0MyMy41ODk4IDEwNC45OCAyMy4xODc1IDEwNS41MDQgMjIuNjY0MSAxMDUuOTM0QzIyLjE0MDYgMTA2LjM2MyAyMS41MjczIDEwNi42ODggMjAuODI0MiAxMDYuOTA2QzIwLjEyODkgMTA3LjEyNSAxOS4zNzExIDEwNy4yMzQgMTguNTUwOCAxMDcuMjM0QzE3LjgxNjQgMTA3LjIzNCAxNy4xMTMzIDEwNy4xMzMgMTYuNDQxNCAxMDYuOTNDMTUuNzY5NSAxMDYuNzI3IDE1LjE2OCAxMDYuNDI2IDE0LjYzNjcgMTA2LjAyN0MxNC4xMDU1IDEwNS42MjEgMTMuNjgzNiAxMDUuMTE3IDEzLjM3MTEgMTA0LjUxNkMxMy4wNjY0IDEwMy45MDYgMTIuOTE0MSAxMDMuMjAzIDEyLjkxNDEgMTAyLjQwNkgxNS43MzgzQzE1LjczODMgMTAyLjkxNCAxNS44NTU1IDEwMy4zNjMgMTYuMDg5OCAxMDMuNzU0QzE2LjMzMiAxMDQuMTM3IDE2LjY2OCAxMDQuNDM4IDE3LjA5NzcgMTA0LjY1NkMxNy41MzUyIDEwNC44NzUgMTguMDM1MiAxMDQuOTg0IDE4LjU5NzcgMTA0Ljk4NEMxOS4xOTE0IDEwNC45ODQgMTkuNzAzMSAxMDQuODc5IDIwLjEzMjggMTA0LjY2OEMyMC41NjI1IDEwNC40NTcgMjAuODkwNiAxMDQuMTQ1IDIxLjExNzIgMTAzLjczQzIxLjM1MTYgMTAzLjMxNiAyMS40Njg4IDEwMi44MTYgMjEuNDY4OCAxMDIuMjNDMjEuNDY4OCAxMDEuNTY2IDIxLjMzOTggMTAxLjAyNyAyMS4wODIgMTAwLjYxM0MyMC44MjQyIDEwMC4xOTkgMjAuNDU3IDk5Ljg5NDUgMTkuOTgwNSA5OS42OTkyQzE5LjUwMzkgOTkuNDk2MSAxOC45NDE0IDk5LjM5NDUgMTguMjkzIDk5LjM5NDVIMTYuNjA1NVpNMzUuNDYwOSA4OS44MzJIMzUuODEyNVY5Mi4xNDA2SDM1LjYxMzNDMzQuNjA1NSA5Mi4xNDA2IDMzLjczNDQgOTIuMjk2OSAzMyA5Mi42MDk0QzMyLjI3MzQgOTIuOTIxOSAzMS42NzU4IDkzLjM1MTYgMzEuMjA3IDkzLjg5ODRDMzAuNzM4MyA5NC40NDUzIDMwLjM4NjcgOTUuMDg1OSAzMC4xNTIzIDk1LjgyMDNDMjkuOTI1OCA5Ni41NDY5IDI5LjgxMjUgOTcuMzIwMyAyOS44MTI1IDk4LjE0MDZWMTAwLjgyNEMyOS44MTI1IDEwMS41MDQgMjkuODg2NyAxMDIuMTA1IDMwLjAzNTIgMTAyLjYyOUMzMC4xODM2IDEwMy4xNDUgMzAuMzkwNiAxMDMuNTc4IDMwLjY1NjIgMTAzLjkzQzMwLjkyOTcgMTA0LjI3MyAzMS4yNDIyIDEwNC41MzUgMzEuNTkzOCAxMDQuNzE1QzMxLjk0NTMgMTA0Ljg5NSAzMi4zMjQyIDEwNC45ODQgMzIuNzMwNSAxMDQuOTg0QzMzLjE1MjMgMTA0Ljk4NCAzMy41MzUyIDEwNC44OTggMzMuODc4OSAxMDQuNzI3QzM0LjIyMjcgMTA0LjU0NyAzNC41MTU2IDEwNC4zMDEgMzQuNzU3OCAxMDMuOTg4QzM1IDEwMy42NzYgMzUuMTgzNiAxMDMuMzA1IDM1LjMwODYgMTAyLjg3NUMzNS40MzM2IDEwMi40NDUgMzUuNDk2MSAxMDEuOTc3IDM1LjQ5NjEgMTAxLjQ2OUMzNS40OTYxIDEwMC45ODQgMzUuNDMzNiAxMDAuNTMxIDM1LjMwODYgMTAwLjEwOUMzNS4xOTE0IDk5LjY3OTcgMzUuMDE1NiA5OS4zMDQ3IDM0Ljc4MTIgOTguOTg0NEMzNC41NDY5IDk4LjY1NjIgMzQuMjUzOSA5OC40MDIzIDMzLjkwMjMgOTguMjIyN0MzMy41NTg2IDk4LjAzNTIgMzMuMTYwMiA5Ny45NDE0IDMyLjcwNyA5Ny45NDE0QzMyLjE0NDUgOTcuOTQxNCAzMS42MzI4IDk4LjA3NDIgMzEuMTcxOSA5OC4zMzk4QzMwLjcxODggOTguNjA1NSAzMC4zNTE2IDk4Ljk1MzEgMzAuMDcwMyA5OS4zODI4QzI5Ljc5NjkgOTkuODA0NyAyOS42NDg0IDEwMC4yNTQgMjkuNjI1IDEwMC43M0wyOC41NDY5IDEwMC4zNzlDMjguNjA5NCA5OS42NTIzIDI4Ljc2OTUgOTkgMjkuMDI3MyA5OC40MjE5QzI5LjI5MyA5Ny44NDM4IDI5LjY0MDYgOTcuMzUxNiAzMC4wNzAzIDk2Ljk0NTNDMzAuNSA5Ni41MzkxIDMwLjk5NjEgOTYuMjMwNSAzMS41NTg2IDk2LjAxOTVDMzIuMTI4OSA5NS44MDA4IDMyLjc1MzkgOTUuNjkxNCAzMy40MzM2IDk1LjY5MTRDMzQuMjYxNyA5NS42OTE0IDM0Ljk4MDUgOTUuODQ3NyAzNS41ODk4IDk2LjE2MDJDMzYuMTk5MiA5Ni40NzI3IDM2LjcwMzEgOTYuODk4NCAzNy4xMDE2IDk3LjQzNzVDMzcuNTA3OCA5Ny45Njg4IDM3LjgwODYgOTguNTc4MSAzOC4wMDM5IDk5LjI2NTZDMzguMjA3IDk5Ljk0NTMgMzguMzA4NiAxMDAuNjU2IDM4LjMwODYgMTAxLjM5OEMzOC4zMDg2IDEwMi4yMTkgMzguMTgzNiAxMDIuOTg0IDM3LjkzMzYgMTAzLjY5NUMzNy42ODM2IDEwNC4zOTggMzcuMzE2NCAxMDUuMDE2IDM2LjgzMiAxMDUuNTQ3QzM2LjM1NTUgMTA2LjA3OCAzNS43NzM0IDEwNi40OTIgMzUuMDg1OSAxMDYuNzg5QzM0LjQwNjIgMTA3LjA4NiAzMy42MzI4IDEwNy4yMzQgMzIuNzY1NiAxMDcuMjM0QzMxLjg1MTYgMTA3LjIzNCAzMS4wMzUyIDEwNy4wNTkgMzAuMzE2NCAxMDYuNzA3QzI5LjYwNTUgMTA2LjM1NSAyOSAxMDUuODcxIDI4LjUgMTA1LjI1NEMyOC4wMDc4IDEwNC42MzcgMjcuNjMyOCAxMDMuOTI2IDI3LjM3NSAxMDMuMTIxQzI3LjExNzIgMTAyLjMxNiAyNi45ODgzIDEwMS40NjUgMjYuOTg4MyAxMDAuNTY2Vjk5LjM5NDVDMjYuOTg4MyA5OC4wOTc3IDI3LjE1MjMgOTYuODc1IDI3LjQ4MDUgOTUuNzI2NkMyNy44MDg2IDk0LjU3MDMgMjguMzEyNSA5My41NTA4IDI4Ljk5MjIgOTIuNjY4QzI5LjY3OTcgOTEuNzg1MiAzMC41NTg2IDkxLjA5MzggMzEuNjI4OSA5MC41OTM4QzMyLjY5OTIgOTAuMDg1OSAzMy45NzY2IDg5LjgzMiAzNS40NjA5IDg5LjgzMloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTQyLjcxNzggOTkuNDg3M1Y5OC45NjA5QzQyLjcxNzggOTguNTgyNyA0Mi43OTk4IDk4LjIzODYgNDIuOTYzOSA5Ny45Mjg3QzQzLjEyNzkgOTcuNjE4OCA0My4zNjI2IDk3LjM3MDQgNDMuNjY4IDk3LjE4MzZDNDMuOTczMyA5Ni45OTY3IDQ0LjMzNTYgOTYuOTAzMyA0NC43NTQ5IDk2LjkwMzNDNDUuMTgzMyA5Ni45MDMzIDQ1LjU0NzkgOTYuOTk2NyA0NS44NDg2IDk3LjE4MzZDNDYuMTU0IDk3LjM3MDQgNDYuMzg4NyA5Ny42MTg4IDQ2LjU1MjcgOTcuOTI4N0M0Ni43MTY4IDk4LjIzODYgNDYuNzk4OCA5OC41ODI3IDQ2Ljc5ODggOTguOTYwOVY5OS40ODczQzQ2Ljc5ODggOTkuODU2NCA0Ni43MTY4IDEwMC4xOTYgNDYuNTUyNyAxMDAuNTA2QzQ2LjM5MzIgMTAwLjgxNiA0Ni4xNjA4IDEwMS4wNjQgNDUuODU1NSAxMDEuMjUxQzQ1LjU1NDcgMTAxLjQzOCA0NS4xOTI0IDEwMS41MzEgNDQuNzY4NiAxMDEuNTMxQzQ0LjM0NDcgMTAxLjUzMSA0My45Nzc5IDEwMS40MzggNDMuNjY4IDEwMS4yNTFDNDMuMzYyNiAxMDEuMDY0IDQzLjEyNzkgMTAwLjgxNiA0Mi45NjM5IDEwMC41MDZDNDIuNzk5OCAxMDAuMTk2IDQyLjcxNzggOTkuODU2NCA0Mi43MTc4IDk5LjQ4NzNaTTQzLjY2OCA5OC45NjA5Vjk5LjQ4NzNDNDMuNjY4IDk5LjY5NjkgNDMuNzA2NyA5OS44OTUyIDQzLjc4NDIgMTAwLjA4MkM0My44NjYyIDEwMC4yNjkgNDMuOTg5MyAxMDAuNDIyIDQ0LjE1MzMgMTAwLjU0QzQ0LjMxNzQgMTAwLjY1NCA0NC41MjI1IDEwMC43MTEgNDQuNzY4NiAxMDAuNzExQzQ1LjAxNDYgMTAwLjcxMSA0NS4yMTc0IDEwMC42NTQgNDUuMzc3IDEwMC41NEM0NS41MzY1IDEwMC40MjIgNDUuNjU0OSAxMDAuMjY5IDQ1LjczMjQgMTAwLjA4MkM0NS44MDk5IDk5Ljg5NTIgNDUuODQ4NiA5OS42OTY5IDQ1Ljg0ODYgOTkuNDg3M1Y5OC45NjA5QzQ1Ljg0ODYgOTguNzQ2NyA0NS44MDc2IDk4LjU0NjIgNDUuNzI1NiA5OC4zNTk0QzQ1LjY0ODEgOTguMTY4IDQ1LjUyNzMgOTguMDE1MyA0NS4zNjMzIDk3LjkwMTRDNDUuMjAzOCA5Ny43ODI5IDQ1LjAwMSA5Ny43MjM2IDQ0Ljc1NDkgOTcuNzIzNkM0NC41MTMzIDk3LjcyMzYgNDQuMzEwNSA5Ny43ODI5IDQ0LjE0NjUgOTcuOTAxNEM0My45ODcgOTguMDE1MyA0My44NjYyIDk4LjE2OCA0My43ODQyIDk4LjM1OTRDNDMuNzA2NyA5OC41NDYyIDQzLjY2OCA5OC43NDY3IDQzLjY2OCA5OC45NjA5Wk00Ny41NjQ1IDEwNS4wOTNWMTA0LjU2QzQ3LjU2NDUgMTA0LjE4NiA0Ny42NDY1IDEwMy44NDQgNDcuODEwNSAxMDMuNTM0QzQ3Ljk3NDYgMTAzLjIyNCA0OC4yMDkzIDEwMi45NzYgNDguNTE0NiAxMDIuNzg5QzQ4LjgyIDEwMi42MDIgNDkuMTgyMyAxMDIuNTA5IDQ5LjYwMTYgMTAyLjUwOUM1MC4wMjk5IDEwMi41MDkgNTAuMzk0NSAxMDIuNjAyIDUwLjY5NTMgMTAyLjc4OUM1MS4wMDA3IDEwMi45NzYgNTEuMjM1NCAxMDMuMjI0IDUxLjM5OTQgMTAzLjUzNEM1MS41NjM1IDEwMy44NDQgNTEuNjQ1NSAxMDQuMTg2IDUxLjY0NTUgMTA0LjU2VjEwNS4wOTNDNTEuNjQ1NSAxMDUuNDY2IDUxLjU2MzUgMTA1LjgwOCA1MS4zOTk0IDEwNi4xMThDNTEuMjM5OSAxMDYuNDI4IDUxLjAwNzUgMTA2LjY3NiA1MC43MDIxIDEwNi44NjNDNTAuNDAxNCAxMDcuMDUgNTAuMDM5MSAxMDcuMTQ0IDQ5LjYxNTIgMTA3LjE0NEM0OS4xOTE0IDEwNy4xNDQgNDguODI2OCAxMDcuMDUgNDguNTIxNSAxMDYuODYzQzQ4LjIxNjEgMTA2LjY3NiA0Ny45NzkyIDEwNi40MjggNDcuODEwNSAxMDYuMTE4QzQ3LjY0NjUgMTA1LjgwOCA0Ny41NjQ1IDEwNS40NjYgNDcuNTY0NSAxMDUuMDkzWk00OC41MTQ2IDEwNC41NlYxMDUuMDkzQzQ4LjUxNDYgMTA1LjMwMiA0OC41NTM0IDEwNS41MDMgNDguNjMwOSAxMDUuNjk0QzQ4LjcxMjkgMTA1Ljg4MSA0OC44MzU5IDEwNi4wMzQgNDkgMTA2LjE1MkM0OS4xNjQxIDEwNi4yNjYgNDkuMzY5MSAxMDYuMzIzIDQ5LjYxNTIgMTA2LjMyM0M0OS44NjEzIDEwNi4zMjMgNTAuMDY0MSAxMDYuMjY2IDUwLjIyMzYgMTA2LjE1MkM1MC4zODc3IDEwNi4wMzQgNTAuNTA4NSAxMDUuODgxIDUwLjU4NTkgMTA1LjY5NEM1MC42NjM0IDEwNS41MDcgNTAuNzAyMSAxMDUuMzA3IDUwLjcwMjEgMTA1LjA5M1YxMDQuNTZDNTAuNzAyMSAxMDQuMzQ1IDUwLjY2MTEgMTA0LjE0NSA1MC41NzkxIDEwMy45NThDNTAuNTAxNiAxMDMuNzcxIDUwLjM4MDkgMTAzLjYyMSA1MC4yMTY4IDEwMy41MDdDNTAuMDU3MyAxMDMuMzg4IDQ5Ljg1MjIgMTAzLjMyOSA0OS42MDE2IDEwMy4zMjlDNDkuMzYgMTAzLjMyOSA0OS4xNTcyIDEwMy4zODggNDguOTkzMiAxMDMuNTA3QzQ4LjgzMzcgMTAzLjYyMSA0OC43MTI5IDEwMy43NzEgNDguNjMwOSAxMDMuOTU4QzQ4LjU1MzQgMTA0LjE0NSA0OC41MTQ2IDEwNC4zNDUgNDguNTE0NiAxMDQuNTZaTTQ5LjkxNiA5OC40NjE5TDQ1LjA1NTcgMTA2LjI0MUw0NC4zNDQ3IDEwNS43OUw0OS4yMDUxIDk4LjAxMDdMNDkuOTE2IDk4LjQ2MTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxyZWN0IHg9IjEyIiB5PSIxMjMiIHdpZHRoPSIxNzYiIGhlaWdodD0iNSIgcng9IjIuNSIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC4wNCIvPgo8cmVjdCB3aWR0aD0iNTEiIGhlaWdodD0iNSIgcng9IjIuNSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgNjMgMTIzKSIgZmlsbD0iIzNGNTJERCIvPgo8cGF0aCBkPSJNMTcuNTU5MSAxMzkuNDY3VjE0MC42NTlDMTcuNTU5MSAxNDEuMyAxNy41MDE4IDE0MS44NDEgMTcuMzg3MiAxNDIuMjgxQzE3LjI3MjYgMTQyLjcyMiAxNy4xMDc5IDE0My4wNzYgMTYuODkzMSAxNDMuMzQ1QzE2LjY3ODIgMTQzLjYxMyAxNi40MTg2IDE0My44MDggMTYuMTE0MyAxNDMuOTNDMTUuODEzNSAxNDQuMDQ4IDE1LjQ3MzMgMTQ0LjEwNyAxNS4wOTM4IDE0NC4xMDdDMTQuNzkzIDE0NC4xMDcgMTQuNTE1NSAxNDQuMDcgMTQuMjYxMiAxNDMuOTk1QzE0LjAwNyAxNDMuOTE5IDEzLjc3NzggMTQzLjc5OSAxMy41NzM3IDE0My42MzVDMTMuMzczMiAxNDMuNDY2IDEzLjIwMTMgMTQzLjI0OCAxMy4wNTgxIDE0Mi45NzlDMTIuOTE0OSAxNDIuNzExIDEyLjgwNTcgMTQyLjM4NSAxMi43MzA1IDE0Mi4wMDJDMTIuNjU1MyAxNDEuNjE5IDEyLjYxNzcgMTQxLjE3MSAxMi42MTc3IDE0MC42NTlWMTM5LjQ2N0MxMi42MTc3IDEzOC44MjYgMTIuNjc1IDEzOC4yODkgMTIuNzg5NiAxMzcuODU1QzEyLjkwNzcgMTM3LjQyMiAxMy4wNzQyIDEzNy4wNzUgMTMuMjg5MSAxMzYuODEzQzEzLjUwMzkgMTM2LjU0OSAxMy43NjE3IDEzNi4zNTkgMTQuMDYyNSAxMzYuMjQ0QzE0LjM2NjkgMTM2LjEzIDE0LjcwNyAxMzYuMDcyIDE1LjA4MyAxMzYuMDcyQzE1LjM4NzQgMTM2LjA3MiAxNS42NjY3IDEzNi4xMSAxNS45MjA5IDEzNi4xODVDMTYuMTc4NyAxMzYuMjU3IDE2LjQwNzkgMTM2LjM3MyAxNi42MDg0IDEzNi41MzRDMTYuODA4OSAxMzYuNjkyIDE2Ljk3OSAxMzYuOTAzIDE3LjExODcgMTM3LjE2OEMxNy4yNjE5IDEzNy40MjkgMTcuMzcxMSAxMzcuNzUgMTcuNDQ2MyAxMzguMTI5QzE3LjUyMTUgMTM4LjUwOSAxNy41NTkxIDEzOC45NTUgMTcuNTU5MSAxMzkuNDY3Wk0xNi41NjAxIDE0MC44MlYxMzkuM0MxNi41NjAxIDEzOC45NDkgMTYuNTM4NiAxMzguNjQxIDE2LjQ5NTYgMTM4LjM3NkMxNi40NTYyIDEzOC4xMDggMTYuMzk3MSAxMzcuODc5IDE2LjMxODQgMTM3LjY4OUMxNi4yMzk2IDEzNy40OTkgMTYuMTM5MyAxMzcuMzQ1IDE2LjAxNzYgMTM3LjIyN0MxNS44OTk0IDEzNy4xMDkgMTUuNzYxNiAxMzcuMDIzIDE1LjYwNCAxMzYuOTY5QzE1LjQ1IDEzNi45MTIgMTUuMjc2NCAxMzYuODgzIDE1LjA4MyAxMzYuODgzQzE0Ljg0NjcgMTM2Ljg4MyAxNC42MzcyIDEzNi45MjggMTQuNDU0NiAxMzcuMDE4QzE0LjI3MiAxMzcuMTA0IDE0LjExOCAxMzcuMjQxIDEzLjk5MjcgMTM3LjQzMUMxMy44NzA5IDEzNy42MjEgMTMuNzc3OCAxMzcuODcgMTMuNzEzNCAxMzguMTc4QzEzLjY0ODkgMTM4LjQ4NiAxMy42MTY3IDEzOC44NiAxMy42MTY3IDEzOS4zVjE0MC44MkMxMy42MTY3IDE0MS4xNzEgMTMuNjM2NCAxNDEuNDgxIDEzLjY3NTggMTQxLjc1QzEzLjcxODggMTQyLjAxOCAxMy43ODE0IDE0Mi4yNTEgMTMuODYzOCAxNDIuNDQ4QzEzLjk0NjEgMTQyLjY0MSAxNC4wNDY0IDE0Mi44IDE0LjE2NDYgMTQyLjkyNkMxNC4yODI3IDE0My4wNTEgMTQuNDE4OCAxNDMuMTQ0IDE0LjU3MjggMTQzLjIwNUMxNC43MzAzIDE0My4yNjIgMTQuOTA0IDE0My4yOTEgMTUuMDkzOCAxNDMuMjkxQzE1LjMzNzIgMTQzLjI5MSAxNS41NTAzIDE0My4yNDQgMTUuNzMyOSAxNDMuMTUxQzE1LjkxNTUgMTQzLjA1OCAxNi4wNjc3IDE0Mi45MTMgMTYuMTg5NSAxNDIuNzE2QzE2LjMxNDggMTQyLjUxNiAxNi40MDc5IDE0Mi4yNiAxNi40Njg4IDE0MS45NDhDMTYuNTI5NiAxNDEuNjMzIDE2LjU2MDEgMTQxLjI1NyAxNi41NjAxIDE0MC44MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTE3MS45MTYgMTM2LjEzN1YxNDRIMTcwLjkyMlYxMzcuMzc3TDE2OC45MTggMTM4LjEwOFYxMzcuMjExTDE3MS43NiAxMzYuMTM3SDE3MS45MTZaTTE3OS45OTcgMTM5LjQ2N1YxNDAuNjU5QzE3OS45OTcgMTQxLjMgMTc5LjkzOSAxNDEuODQxIDE3OS44MjUgMTQyLjI4MUMxNzkuNzEgMTQyLjcyMiAxNzkuNTQ1IDE0My4wNzYgMTc5LjMzMSAxNDMuMzQ1QzE3OS4xMTYgMTQzLjYxMyAxNzguODU2IDE0My44MDggMTc4LjU1MiAxNDMuOTNDMTc4LjI1MSAxNDQuMDQ4IDE3Ny45MTEgMTQ0LjEwNyAxNzcuNTMxIDE0NC4xMDdDMTc3LjIzIDE0NC4xMDcgMTc2Ljk1MyAxNDQuMDcgMTc2LjY5OSAxNDMuOTk1QzE3Ni40NDQgMTQzLjkxOSAxNzYuMjE1IDE0My43OTkgMTc2LjAxMSAxNDMuNjM1QzE3NS44MTEgMTQzLjQ2NiAxNzUuNjM5IDE0My4yNDggMTc1LjQ5NiAxNDIuOTc5QzE3NS4zNTIgMTQyLjcxMSAxNzUuMjQzIDE0Mi4zODUgMTc1LjE2OCAxNDIuMDAyQzE3NS4wOTMgMTQxLjYxOSAxNzUuMDU1IDE0MS4xNzEgMTc1LjA1NSAxNDAuNjU5VjEzOS40NjdDMTc1LjA1NSAxMzguODI2IDE3NS4xMTIgMTM4LjI4OSAxNzUuMjI3IDEzNy44NTVDMTc1LjM0NSAxMzcuNDIyIDE3NS41MTIgMTM3LjA3NSAxNzUuNzI3IDEzNi44MTNDMTc1Ljk0MSAxMzYuNTQ5IDE3Ni4xOTkgMTM2LjM1OSAxNzYuNSAxMzYuMjQ0QzE3Ni44MDQgMTM2LjEzIDE3Ny4xNDUgMTM2LjA3MiAxNzcuNTIxIDEzNi4wNzJDMTc3LjgyNSAxMzYuMDcyIDE3OC4xMDQgMTM2LjExIDE3OC4zNTggMTM2LjE4NUMxNzguNjE2IDEzNi4yNTcgMTc4Ljg0NSAxMzYuMzczIDE3OS4wNDYgMTM2LjUzNEMxNzkuMjQ2IDEzNi42OTIgMTc5LjQxNyAxMzYuOTAzIDE3OS41NTYgMTM3LjE2OEMxNzkuNjk5IDEzNy40MjkgMTc5LjgwOSAxMzcuNzUgMTc5Ljg4NCAxMzguMTI5QzE3OS45NTkgMTM4LjUwOSAxNzkuOTk3IDEzOC45NTUgMTc5Ljk5NyAxMzkuNDY3Wk0xNzguOTk4IDE0MC44MlYxMzkuM0MxNzguOTk4IDEzOC45NDkgMTc4Ljk3NiAxMzguNjQxIDE3OC45MzMgMTM4LjM3NkMxNzguODk0IDEzOC4xMDggMTc4LjgzNSAxMzcuODc5IDE3OC43NTYgMTM3LjY4OUMxNzguNjc3IDEzNy40OTkgMTc4LjU3NyAxMzcuMzQ1IDE3OC40NTUgMTM3LjIyN0MxNzguMzM3IDEzNy4xMDkgMTc4LjE5OSAxMzcuMDIzIDE3OC4wNDIgMTM2Ljk2OUMxNzcuODg4IDEzNi45MTIgMTc3LjcxNCAxMzYuODgzIDE3Ny41MjEgMTM2Ljg4M0MxNzcuMjg0IDEzNi44ODMgMTc3LjA3NSAxMzYuOTI4IDE3Ni44OTIgMTM3LjAxOEMxNzYuNzA5IDEzNy4xMDQgMTc2LjU1NiAxMzcuMjQxIDE3Ni40MyAxMzcuNDMxQzE3Ni4zMDggMTM3LjYyMSAxNzYuMjE1IDEzNy44NyAxNzYuMTUxIDEzOC4xNzhDMTc2LjA4NiAxMzguNDg2IDE3Ni4wNTQgMTM4Ljg2IDE3Ni4wNTQgMTM5LjNWMTQwLjgyQzE3Ni4wNTQgMTQxLjE3MSAxNzYuMDc0IDE0MS40ODEgMTc2LjExMyAxNDEuNzVDMTc2LjE1NiAxNDIuMDE4IDE3Ni4yMTkgMTQyLjI1MSAxNzYuMzAxIDE0Mi40NDhDMTc2LjM4NCAxNDIuNjQxIDE3Ni40ODQgMTQyLjggMTc2LjYwMiAxNDIuOTI2QzE3Ni43MiAxNDMuMDUxIDE3Ni44NTYgMTQzLjE0NCAxNzcuMDEgMTQzLjIwNUMxNzcuMTY4IDE0My4yNjIgMTc3LjM0MSAxNDMuMjkxIDE3Ny41MzEgMTQzLjI5MUMxNzcuNzc1IDE0My4yOTEgMTc3Ljk4OCAxNDMuMjQ0IDE3OC4xNyAxNDMuMTUxQzE3OC4zNTMgMTQzLjA1OCAxNzguNTA1IDE0Mi45MTMgMTc4LjYyNyAxNDIuNzE2QzE3OC43NTIgMTQyLjUxNiAxNzguODQ1IDE0Mi4yNiAxNzguOTA2IDE0MS45NDhDMTc4Ljk2NyAxNDEuNjMzIDE3OC45OTggMTQxLjI1NyAxNzguOTk4IDE0MC44MlpNMTg2LjQzNCAxMzkuNDY3VjE0MC42NTlDMTg2LjQzNCAxNDEuMyAxODYuMzc3IDE0MS44NDEgMTg2LjI2MiAxNDIuMjgxQzE4Ni4xNDggMTQyLjcyMiAxODUuOTgzIDE0My4wNzYgMTg1Ljc2OCAxNDMuMzQ1QzE4NS41NTMgMTQzLjYxMyAxODUuMjk0IDE0My44MDggMTg0Ljk4OSAxNDMuOTNDMTg0LjY4OCAxNDQuMDQ4IDE4NC4zNDggMTQ0LjEwNyAxODMuOTY5IDE0NC4xMDdDMTgzLjY2OCAxNDQuMTA3IDE4My4zOSAxNDQuMDcgMTgzLjEzNiAxNDMuOTk1QzE4Mi44ODIgMTQzLjkxOSAxODIuNjUzIDE0My43OTkgMTgyLjQ0OSAxNDMuNjM1QzE4Mi4yNDggMTQzLjQ2NiAxODIuMDc2IDE0My4yNDggMTgxLjkzMyAxNDIuOTc5QzE4MS43OSAxNDIuNzExIDE4MS42ODEgMTQyLjM4NSAxODEuNjA1IDE0Mi4wMDJDMTgxLjUzIDE0MS42MTkgMTgxLjQ5MyAxNDEuMTcxIDE4MS40OTMgMTQwLjY1OVYxMzkuNDY3QzE4MS40OTMgMTM4LjgyNiAxODEuNTUgMTM4LjI4OSAxODEuNjY1IDEzNy44NTVDMTgxLjc4MyAxMzcuNDIyIDE4MS45NDkgMTM3LjA3NSAxODIuMTY0IDEzNi44MTNDMTgyLjM3OSAxMzYuNTQ5IDE4Mi42MzcgMTM2LjM1OSAxODIuOTM4IDEzNi4yNDRDMTgzLjI0MiAxMzYuMTMgMTgzLjU4MiAxMzYuMDcyIDE4My45NTggMTM2LjA3MkMxODQuMjYyIDEzNi4wNzIgMTg0LjU0MiAxMzYuMTEgMTg0Ljc5NiAxMzYuMTg1QzE4NS4wNTQgMTM2LjI1NyAxODUuMjgzIDEzNi4zNzMgMTg1LjQ4MyAxMzYuNTM0QzE4NS42ODQgMTM2LjY5MiAxODUuODU0IDEzNi45MDMgMTg1Ljk5NCAxMzcuMTY4QzE4Ni4xMzcgMTM3LjQyOSAxODYuMjQ2IDEzNy43NSAxODYuMzIxIDEzOC4xMjlDMTg2LjM5NiAxMzguNTA5IDE4Ni40MzQgMTM4Ljk1NSAxODYuNDM0IDEzOS40NjdaTTE4NS40MzUgMTQwLjgyVjEzOS4zQzE4NS40MzUgMTM4Ljk0OSAxODUuNDE0IDEzOC42NDEgMTg1LjM3MSAxMzguMzc2QzE4NS4zMzEgMTM4LjEwOCAxODUuMjcyIDEzNy44NzkgMTg1LjE5MyAxMzcuNjg5QzE4NS4xMTUgMTM3LjQ5OSAxODUuMDE0IDEzNy4zNDUgMTg0Ljg5MyAxMzcuMjI3QzE4NC43NzQgMTM3LjEwOSAxODQuNjM3IDEzNy4wMjMgMTg0LjQ3OSAxMzYuOTY5QzE4NC4zMjUgMTM2LjkxMiAxODQuMTUxIDEzNi44ODMgMTgzLjk1OCAxMzYuODgzQzE4My43MjIgMTM2Ljg4MyAxODMuNTEyIDEzNi45MjggMTgzLjMzIDEzNy4wMThDMTgzLjE0NyAxMzcuMTA0IDE4Mi45OTMgMTM3LjI0MSAxODIuODY4IDEzNy40MzFDMTgyLjc0NiAxMzcuNjIxIDE4Mi42NTMgMTM3Ljg3IDE4Mi41ODggMTM4LjE3OEMxODIuNTI0IDEzOC40ODYgMTgyLjQ5MiAxMzguODYgMTgyLjQ5MiAxMzkuM1YxNDAuODJDMTgyLjQ5MiAxNDEuMTcxIDE4Mi41MTEgMTQxLjQ4MSAxODIuNTUxIDE0MS43NUMxODIuNTk0IDE0Mi4wMTggMTgyLjY1NiAxNDIuMjUxIDE4Mi43MzkgMTQyLjQ0OEMxODIuODIxIDE0Mi42NDEgMTgyLjkyMSAxNDIuOCAxODMuMDQgMTQyLjkyNkMxODMuMTU4IDE0My4wNTEgMTgzLjI5NCAxNDMuMTQ0IDE4My40NDggMTQzLjIwNUMxODMuNjA1IDE0My4yNjIgMTgzLjc3OSAxNDMuMjkxIDE4My45NjkgMTQzLjI5MUMxODQuMjEyIDE0My4yOTEgMTg0LjQyNSAxNDMuMjQ0IDE4NC42MDggMTQzLjE1MUMxODQuNzkxIDE0My4wNTggMTg0Ljk0MyAxNDIuOTEzIDE4NS4wNjQgMTQyLjcxNkMxODUuMTkgMTQyLjUxNiAxODUuMjgzIDE0Mi4yNiAxODUuMzQ0IDE0MS45NDhDMTg1LjQwNSAxNDEuNjMzIDE4NS40MzUgMTQxLjI1NyAxODUuNDM1IDE0MC44MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8L2c+CjxyZWN0IHg9IjAuNSIgeT0iMC41IiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgcng9IjMuNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPGRlZnM+CjxmaWx0ZXIgaWQ9ImZpbHRlcjBfZF80Njg1XzQwOTg5IiB4PSItOCIgeT0iLTQiIHdpZHRoPSIyMTYiIGhlaWdodD0iMTc2IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQgZHk9IjQiLz4KPGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNCIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuMDQgMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd180Njg1XzQwOTg5Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzQ2ODVfNDA5ODkiIHJlc3VsdD0ic2hhcGUiLz4KPC9maWx0ZXI+CjxmaWx0ZXIgaWQ9ImZpbHRlcjFfYl80Njg1XzQwOTg5IiB4PSI2IiB5PSI2IiB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+CjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80Njg1XzQwOTg5Ii8+CjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80Njg1XzQwOTg5IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzQ2ODVfNDA5ODkiPgo8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", "description": "Displays any value reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", "descriptor": { "type": "latest", @@ -19,7 +19,6 @@ "basicModeDirective": "tb-progress-bar-basic-config", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Progress bar\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" }, - "externalId": null, "tags": [ "progress", "loading", diff --git a/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json b/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json index e19ff918be..a17d2ee031 100644 --- a/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json +++ b/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json @@ -2,7 +2,7 @@ "fqn": "simple_value_and_chart_card", "name": "Simple Value and chart card", "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAflBMVEUAAADg4ODg4ODf39/////g4OAhISHj4+M/Ut09PT2srKx0dHSQkJDn6fvHx8fx8fEvLy9XaOHP1PdYWFi3vvKenp7z9P3V1dW6urqCgoKHk+pvfeVLXd/b3/lmZmaTnuyrs/DDyfWfqO5jc+Orq6tLS0ufqe5LXN97iOhKSkrA29V9AAAABHRSTlMA77cggcfpsAAABRRJREFUeNrsz0EBACEIADBBedu/7RlDua3BBgAAAAAAwO/MjHpdzvOIvZ63Y45s8DiTHLVaiC6RErmMyG0+9stdRXIYiKLJ5RZ6IBUIITlx1A7m/39wq3A3y/ZqN5jA0/M4ILnqWhYc5MD+EXk1vqZIlTv4fPwhUnin4B1IHLie9YnEGFVtEryDwozLWYs4IZxOPUcAJVoxIT0LIFGmpY5dzyB2oOQ8feY+MWIFZrQxRxZPu2DNJSKiVB5AYgjkzkAdiAyqbEA9LAjiAQOyr8nYlNqsKUAj4EsLEpWeLLhCxGdBZkeyIeSwZkPkXtFYsHmqzUWK1JBgaT1frd8i3KRGi0QDVlwiMthEBpOJyBmJNZERmGZ0qIgcegZAHaVRnkV8n51FxPdYcYVIIZ22EPGCJ3eRrhp0LdLofKDIYBJnIVK4QYM4jyAB6V8nMsTAkitEoCpArM8iGyyZNm5AGadIZAbaQ6RbVMNDJPsTMrHiGpEbQwuczyJsB70JXmynSLVGlQOVemDQu4dIfbOF+oYL+Eukdziy7VsBZq5nVPNEZN92DyB9Tzdg5OGNhV7htifL7M7Mj3086xX/5fqPRj+Al+VbipRU8LJ8zf+Rz8wv9ukYBWIYBqJoO40KCePCjhBJYe/9T7ikCCHJBTTg32m6B/aCZGtBsrUg2VqQbOWD+L5bpYdIbDjrhRsS2KK426HoQgxpiOtRlalCCwkc9yGqlRRip+POEaSQrs97QCghjvIc6vxRQtrnTwwYIUTQ3lOdnRDSIJ9twOggf3bNqElVEAzDNwx+CpggUimW5s457f//g0eWU5gszTi7hRc+F/lmTuMz34eQZgvil2T1IkJeeuZfovySrFuk02ComZv+bEH8kqxBhAnRBsZD0zPVnrnubx6afW8MIraIumgY4Z0KL0UYh7odNxd9M/LgTWQRxqHphJBX8BZ/0i1F1BkMT5a6EkRUEca5sElwzuYfTd/1l0vXojADVzFFrq7n2d/GmzAW0MIwNVFCyp69TUSA9K+hrrEWIV1zKtFoMAzyTSI1D11DGR/QQgSHq2wZ688a9LlnTHQD8At7tYg/TXfQuZbXbPnX/eFg0LW4y9VuFgqIHI6E5jbuMpcNFcmQJafkmCVPuqFFU5pbO6nGOi2m7aUU6sHOqIigSJJiQ7lzmZhsOOJPTExISvwFRQGGWfuoGngnTG/o7jfvuHCoAyK7FBc0I+YcjUda2WwdcYUo3lnBE6XjaxX+iTRDDjCiG4Z+FdkFRDL7d4sSF4/ZsMc5ys2u0S23z9CLXXBF7sGEbBV6Db7ICX+aTYWxyaXJdMyWNCVpOm4LTOxoskI+vEZvxm+tJLGjIb1nMmZLQlIy7soxPtiDKd3/YFXxShE34sk8OzJbiDCcowh4IrYIySx74yhMBxK9k7AItb3j8iIRAVcUASPie9CH7Ivsn9WDMxQBX+Rjcu6Vy47DvUh5Pi+NaKBRKAK+yB7j4+SUKfLY3Y7I522nzsB7FANfZF9gMsseJS7shXk2WKSGc5xy+CKJWXdkhvwrZ//zA0mB0yrPysd6tQ00LYqFv0S5QRCd5Ef2qd1/nN5qAN2hWPgiObnxgQ6TPCP5OJWn46RQPf95V63hqa6EQaCFrFKESRSdtdzE3kQ2kbWyiayNTWRtbCJrYxNZG//auQMaAEAYiIFZwvxbZjLgd+egBirkNUEhdSJ0znInZoI0W6r+Xc2WCgAAAAAAYJsLnGVmvd/WiIIAAAAASUVORK5CYII=", + "image": "tb-image:c2ltcGxlX3ZhbHVlX2FuZF9jaGFydF9jYXJkX3N5c3RlbV93aWRnZXRfaW1hZ2UucG5n:IlNpbXBsZSBWYWx1ZSBhbmQgY2hhcnQgY2FyZCIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHk9IjMyIiB3aWR0aD0iMjAwIiBoZWlnaHQ9Ijk2IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIzMi41IiB3aWR0aD0iMTk5IiBoZWlnaHQ9Ijk1IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTYuNzQxNyA0NS43NTc4VjU1SDE1LjE2MTFWNDUuNzU3OEgxNi43NDE3Wk0xOS42NDI2IDQ1Ljc1NzhWNDcuMDI3M0gxMi4yODU2VjQ1Ljc1NzhIMTkuNjQyNlpNMjMuMzc3NCA1NS4xMjdDMjIuODY5NiA1NS4xMjcgMjIuNDEwNSA1NS4wNDQ0IDIyIDU0Ljg3OTRDMjEuNTkzOCA1NC43MTAxIDIxLjI0NjcgNTQuNDc1MyAyMC45NTkgNTQuMTc0OEMyMC42NzU1IDUzLjg3NDMgMjAuNDU3NSA1My41MjEgMjAuMzA1MiA1My4xMTQ3QzIwLjE1MjggNTIuNzA4NSAyMC4wNzY3IDUyLjI3MDUgMjAuMDc2NyA1MS44MDA4VjUxLjU0NjlDMjAuMDc2NyA1MS4wMDk0IDIwLjE1NDkgNTAuNTIyOCAyMC4zMTE1IDUwLjA4NjlDMjAuNDY4MSA0OS42NTEgMjAuNjg2IDQ5LjI3ODYgMjAuOTY1MyA0OC45Njk3QzIxLjI0NDYgNDguNjU2NiAyMS41NzQ3IDQ4LjQxNzUgMjEuOTU1NiA0OC4yNTI0QzIyLjMzNjQgNDguMDg3NCAyMi43NDkgNDguMDA0OSAyMy4xOTM0IDQ4LjAwNDlDMjMuNjg0MiA0OC4wMDQ5IDI0LjExMzggNDguMDg3NCAyNC40ODE5IDQ4LjI1MjRDMjQuODUwMSA0OC40MTc1IDI1LjE1NDggNDguNjUwMiAyNS4zOTYgNDguOTUwN0MyNS42NDE0IDQ5LjI0NjkgMjUuODIzNCA0OS42MDAzIDI1Ljk0MTkgNTAuMDEwN0MyNi4wNjQ2IDUwLjQyMTIgMjYuMTI2IDUwLjg3NCAyNi4xMjYgNTEuMzY5MVY1Mi4wMjI5SDIwLjgxOTNWNTAuOTI0OEgyNC42MTUyVjUwLjgwNDJDMjQuNjA2OCA1MC41MjkxIDI0LjU1MTggNTAuMjcxIDI0LjQ1MDIgNTAuMDI5OEMyNC4zNTI5IDQ5Ljc4ODYgMjQuMjAyNiA0OS41OTM5IDIzLjk5OTUgNDkuNDQ1OEMyMy43OTY0IDQ5LjI5NzcgMjMuNTI1NiA0OS4yMjM2IDIzLjE4NyA0OS4yMjM2QzIyLjkzMzEgNDkuMjIzNiAyMi43MDY3IDQ5LjI3ODYgMjIuNTA3OCA0OS4zODg3QzIyLjMxMzIgNDkuNDk0NSAyMi4xNTAyIDQ5LjY0ODkgMjIuMDE5IDQ5Ljg1MjFDMjEuODg3OSA1MC4wNTUyIDIxLjc4NjMgNTAuMzAwNiAyMS43MTQ0IDUwLjU4ODRDMjEuNjQ2NiA1MC44NzE5IDIxLjYxMjggNTEuMTkxNCAyMS42MTI4IDUxLjU0NjlWNTEuODAwOEMyMS42MTI4IDUyLjEwMTIgMjEuNjUzIDUyLjM4MDUgMjEuNzMzNCA1Mi42Mzg3QzIxLjgxOCA1Mi44OTI2IDIxLjk0MDggNTMuMTE0NyAyMi4xMDE2IDUzLjMwNTJDMjIuMjYyNCA1My40OTU2IDIyLjQ1NyA1My42NDU4IDIyLjY4NTUgNTMuNzU1OUMyMi45MTQxIDUzLjg2MTcgMjMuMTc0MyA1My45MTQ2IDIzLjQ2NjMgNTMuOTE0NkMyMy44MzQ1IDUzLjkxNDYgMjQuMTYyNCA1My44NDA1IDI0LjQ1MDIgNTMuNjkyNEMyNC43MzggNTMuNTQ0MyAyNC45ODc2IDUzLjMzNDggMjUuMTk5MiA1My4wNjRMMjYuMDA1NCA1My44NDQ3QzI1Ljg1NzMgNTQuMDYwNSAyNS42NjQ3IDU0LjI2NzkgMjUuNDI3NyA1NC40NjY4QzI1LjE5MDggNTQuNjYxNSAyNC45MDA5IDU0LjgyMDEgMjQuNTU4MSA1NC45NDI5QzI0LjIxOTYgNTUuMDY1NiAyMy44MjYgNTUuMTI3IDIzLjM3NzQgNTUuMTI3Wk0yOS4wOTkxIDQ5LjUyODNWNTVIMjcuNTY5M1Y0OC4xMzE4SDI5LjAxMDNMMjkuMDk5MSA0OS41MjgzWk0yOC44NTE2IDUxLjMxMkwyOC4zMzExIDUxLjMwNTdDMjguMzMxMSA1MC44MzE3IDI4LjM5MDMgNTAuMzkzNyAyOC41MDg4IDQ5Ljk5MTdDMjguNjI3MyA0OS41ODk3IDI4LjgwMDggNDkuMjQwNiAyOS4wMjkzIDQ4Ljk0NDNDMjkuMjU3OCA0OC42NDM5IDI5LjU0MTMgNDguNDEzMiAyOS44Nzk5IDQ4LjI1MjRDMzAuMjIyNyA0OC4wODc0IDMwLjYxODMgNDguMDA0OSAzMS4wNjY5IDQ4LjAwNDlDMzEuMzggNDguMDA0OSAzMS42NjU3IDQ4LjA1MTQgMzEuOTIzOCA0OC4xNDQ1QzMyLjE4NjIgNDguMjMzNCAzMi40MTI2IDQ4LjM3NTIgMzIuNjAzIDQ4LjU2OThDMzIuNzk3NyA0OC43NjQ1IDMyLjk0NTggNDkuMDE0MiAzMy4wNDc0IDQ5LjMxODhDMzMuMTUzMiA0OS42MjM1IDMzLjIwNjEgNDkuOTkxNyAzMy4yMDYxIDUwLjQyMzNWNTVIMzEuNjc2M1Y1MC41NTY2QzMxLjY3NjMgNTAuMjIyMyAzMS42MjU1IDQ5Ljk2IDMxLjUyMzkgNDkuNzY5NUMzMS40MjY2IDQ5LjU3OTEgMzEuMjg0OCA0OS40NDM3IDMxLjA5ODYgNDkuMzYzM0MzMC45MTY3IDQ5LjI3ODYgMzAuNjk4NyA0OS4yMzYzIDMwLjQ0NDggNDkuMjM2M0MzMC4xNTcxIDQ5LjIzNjMgMjkuOTExNiA0OS4yOTEzIDI5LjcwODUgNDkuNDAxNEMyOS41MDk2IDQ5LjUxMTQgMjkuMzQ2NyA0OS42NjE2IDI5LjIxOTcgNDkuODUyMUMyOS4wOTI4IDUwLjA0MjUgMjguOTk5NyA1MC4yNjI1IDI4Ljk0MDQgNTAuNTEyMkMyOC44ODEyIDUwLjc2MTkgMjguODUxNiA1MS4wMjg1IDI4Ljg1MTYgNTEuMzEyWk0zMy4xMTA4IDUwLjkwNThMMzIuMzkzNiA1MS4wNjQ1QzMyLjM5MzYgNTAuNjQ5NyAzMi40NTA3IDUwLjI1ODMgMzIuNTY0OSA0OS44OTAxQzMyLjY4MzQgNDkuNTE3NyAzMi44NTQ4IDQ5LjE5MTkgMzMuMDc5MSA0OC45MTI2QzMzLjMwNzYgNDguNjI5MSAzMy41ODkgNDguNDA2OSAzMy45MjMzIDQ4LjI0NjFDMzQuMjU3NiA0OC4wODUzIDM0LjY0MDYgNDguMDA0OSAzNS4wNzIzIDQ4LjAwNDlDMzUuNDIzNSA0OC4wMDQ5IDM1LjczNjcgNDguMDUzNSAzNi4wMTE3IDQ4LjE1MDlDMzYuMjkxIDQ4LjI0NCAzNi41MjggNDguMzkyMSAzNi43MjI3IDQ4LjU5NTJDMzYuOTE3MyA0OC43OTgzIDM3LjA2NTQgNDkuMDYyOCAzNy4xNjcgNDkuMzg4N0MzNy4yNjg2IDQ5LjcxMDMgMzcuMzE5MyA1MC4wOTk2IDM3LjMxOTMgNTAuNTU2NlY1NUgzNS43ODMyVjUwLjU1MDNDMzUuNzgzMiA1MC4yMDMzIDM1LjczMjQgNDkuOTM0NiAzNS42MzA5IDQ5Ljc0NDFDMzUuNTMzNSA0OS41NTM3IDM1LjM5MzkgNDkuNDIyNSAzNS4yMTE5IDQ5LjM1MDZDMzUuMDI5OSA0OS4yNzQ0IDM0LjgxMiA0OS4yMzYzIDM0LjU1ODEgNDkuMjM2M0MzNC4zMjExIDQ5LjIzNjMgMzQuMTExNyA0OS4yODA4IDMzLjkyOTcgNDkuMzY5NkMzMy43NTIgNDkuNDU0MyAzMy42MDE3IDQ5LjU3NDkgMzMuNDc5IDQ5LjczMTRDMzMuMzU2MyA0OS44ODM4IDMzLjI2MzIgNTAuMDU5NCAzMy4xOTk3IDUwLjI1ODNDMzMuMTQwNSA1MC40NTcyIDMzLjExMDggNTAuNjczIDMzLjExMDggNTAuOTA1OFpNNDAuNjc5NyA0OS40NTIxVjU3LjY0MDZIMzkuMTQ5OVY0OC4xMzE4SDQwLjU1OTFMNDAuNjc5NyA0OS40NTIxWk00NS4xNTQ4IDUxLjUwMjRWNTEuNjM1N0M0NS4xNTQ4IDUyLjEzNTEgNDUuMDk1NSA1Mi41OTg1IDQ0Ljk3NzEgNTMuMDI1OUM0NC44NjI4IDUzLjQ0OTEgNDQuNjkxNCA1My44MTkzIDQ0LjQ2MjkgNTQuMTM2N0M0NC4yMzg2IDU0LjQ0OTkgNDMuOTYxNCA1NC42OTMyIDQzLjYzMTMgNTQuODY2N0M0My4zMDEzIDU1LjA0MDIgNDIuOTIwNCA1NS4xMjcgNDIuNDg4OCA1NS4xMjdDNDIuMDYxNCA1NS4xMjcgNDEuNjg2OCA1NS4wNDg3IDQxLjM2NTIgNTQuODkyMUM0MS4wNDc5IDU0LjczMTMgNDAuNzc5MSA1NC41MDQ5IDQwLjU1OTEgNTQuMjEyOUM0MC4zMzkgNTMuOTIwOSA0MC4xNjEzIDUzLjU3ODEgNDAuMDI1OSA1My4xODQ2QzM5Ljg5NDcgNTIuNzg2OCAzOS44MDE2IDUyLjM1MDkgMzkuNzQ2NiA1MS44NzdWNTEuMzYyOEMzOS44MDE2IDUwLjg1OTIgMzkuODk0NyA1MC40MDIyIDQwLjAyNTkgNDkuOTkxN0M0MC4xNjEzIDQ5LjU4MTIgNDAuMzM5IDQ5LjIyNzkgNDAuNTU5MSA0OC45MzE2QzQwLjc3OTEgNDguNjM1NCA0MS4wNDc5IDQ4LjQwNjkgNDEuMzY1MiA0OC4yNDYxQzQxLjY4MjYgNDguMDg1MyA0Mi4wNTI5IDQ4LjAwNDkgNDIuNDc2MSA0OC4wMDQ5QzQyLjkwNzcgNDguMDA0OSA0My4yOTA3IDQ4LjA4OTUgNDMuNjI1IDQ4LjI1ODhDNDMuOTU5MyA0OC40MjM4IDQ0LjI0MDcgNDguNjYwOCA0NC40NjkyIDQ4Ljk2OTdDNDQuNjk3OCA0OS4yNzQ0IDQ0Ljg2OTEgNDkuNjQyNiA0NC45ODM0IDUwLjA3NDJDNDUuMDk3NyA1MC41MDE2IDQ1LjE1NDggNTAuOTc3NyA0NS4xNTQ4IDUxLjUwMjRaTTQzLjYyNSA1MS42MzU3VjUxLjUwMjRDNDMuNjI1IDUxLjE4NTEgNDMuNTk1NCA1MC44OTEgNDMuNTM2MSA1MC42MjAxQzQzLjQ3NjkgNTAuMzQ1MSA0My4zODM4IDUwLjEwMzggNDMuMjU2OCA0OS44OTY1QzQzLjEyOTkgNDkuNjg5MSA0Mi45NjcgNDkuNTI4MyA0Mi43NjgxIDQ5LjQxNDFDNDIuNTczNCA0OS4yOTU2IDQyLjMzODUgNDkuMjM2MyA0Mi4wNjM1IDQ5LjIzNjNDNDEuNzkyNiA0OS4yMzYzIDQxLjU1OTkgNDkuMjgyOSA0MS4zNjUyIDQ5LjM3NkM0MS4xNzA2IDQ5LjQ2NDggNDEuMDA3NiA0OS41ODk3IDQwLjg3NjUgNDkuNzUwNUM0MC43NDUzIDQ5LjkxMTMgNDAuNjQzNyA1MC4wOTk2IDQwLjU3MTggNTAuMzE1NEM0MC40OTk4IDUwLjUyNyA0MC40NDkxIDUwLjc1NzYgNDAuNDE5NCA1MS4wMDczVjUyLjIzODhDNDAuNDcwMiA1Mi41NDM1IDQwLjU1NyA1Mi44MjI4IDQwLjY3OTcgNTMuMDc2N0M0MC44MDI0IDUzLjMzMDYgNDAuOTc1OSA1My41MzM3IDQxLjIwMDIgNTMuNjg2QzQxLjQyODcgNTMuODM0MSA0MS43MjA3IDUzLjkwODIgNDIuMDc2MiA1My45MDgyQzQyLjM1MTIgNTMuOTA4MiA0Mi41ODYxIDUzLjg0OSA0Mi43ODA4IDUzLjczMDVDNDIuOTc1NCA1My42MTIgNDMuMTM0MSA1My40NDkxIDQzLjI1NjggNTMuMjQxN0M0My4zODM4IDUzLjAzMDEgNDMuNDc2OSA1Mi43ODY4IDQzLjUzNjEgNTIuNTExN0M0My41OTU0IDUyLjIzNjcgNDMuNjI1IDUxLjk0NDcgNDMuNjI1IDUxLjYzNTdaTTQ5LjczMzkgNTUuMTI3QzQ5LjIyNjEgNTUuMTI3IDQ4Ljc2NjkgNTUuMDQ0NCA0OC4zNTY0IDU0Ljg3OTRDNDcuOTUwMiA1NC43MTAxIDQ3LjYwMzIgNTQuNDc1MyA0Ny4zMTU0IDU0LjE3NDhDNDcuMDMxOSA1My44NzQzIDQ2LjgxNCA1My41MjEgNDYuNjYxNiA1My4xMTQ3QzQ2LjUwOTMgNTIuNzA4NSA0Ni40MzMxIDUyLjI3MDUgNDYuNDMzMSA1MS44MDA4VjUxLjU0NjlDNDYuNDMzMSA1MS4wMDk0IDQ2LjUxMTQgNTAuNTIyOCA0Ni42NjggNTAuMDg2OUM0Ni44MjQ1IDQ5LjY1MSA0Ny4wNDI1IDQ5LjI3ODYgNDcuMzIxOCA0OC45Njk3QzQ3LjYwMTEgNDguNjU2NiA0Ny45MzEyIDQ4LjQxNzUgNDguMzEyIDQ4LjI1MjRDNDguNjkyOSA0OC4wODc0IDQ5LjEwNTUgNDguMDA0OSA0OS41NDk4IDQ4LjAwNDlDNTAuMDQwNyA0OC4wMDQ5IDUwLjQ3MDIgNDguMDg3NCA1MC44Mzg0IDQ4LjI1MjRDNTEuMjA2NSA0OC40MTc1IDUxLjUxMTIgNDguNjUwMiA1MS43NTI0IDQ4Ljk1MDdDNTEuOTk3OSA0OS4yNDY5IDUyLjE3OTkgNDkuNjAwMyA1Mi4yOTgzIDUwLjAxMDdDNTIuNDIxMSA1MC40MjEyIDUyLjQ4MjQgNTAuODc0IDUyLjQ4MjQgNTEuMzY5MVY1Mi4wMjI5SDQ3LjE3NThWNTAuOTI0OEg1MC45NzE3VjUwLjgwNDJDNTAuOTYzMiA1MC41MjkxIDUwLjkwODIgNTAuMjcxIDUwLjgwNjYgNTAuMDI5OEM1MC43MDkzIDQ5Ljc4ODYgNTAuNTU5MSA0OS41OTM5IDUwLjM1NiA0OS40NDU4QzUwLjE1MjggNDkuMjk3NyA0OS44ODIgNDkuMjIzNiA0OS41NDM1IDQ5LjIyMzZDNDkuMjg5NiA0OS4yMjM2IDQ5LjA2MzIgNDkuMjc4NiA0OC44NjQzIDQ5LjM4ODdDNDguNjY5NiA0OS40OTQ1IDQ4LjUwNjcgNDkuNjQ4OSA0OC4zNzU1IDQ5Ljg1MjFDNDguMjQ0MyA1MC4wNTUyIDQ4LjE0MjcgNTAuMzAwNiA0OC4wNzA4IDUwLjU4ODRDNDguMDAzMSA1MC44NzE5IDQ3Ljk2OTIgNTEuMTkxNCA0Ny45NjkyIDUxLjU0NjlWNTEuODAwOEM0Ny45NjkyIDUyLjEwMTIgNDguMDA5NCA1Mi4zODA1IDQ4LjA4OTggNTIuNjM4N0M0OC4xNzQ1IDUyLjg5MjYgNDguMjk3MiA1My4xMTQ3IDQ4LjQ1OCA1My4zMDUyQzQ4LjYxODggNTMuNDk1NiA0OC44MTM1IDUzLjY0NTggNDkuMDQyIDUzLjc1NTlDNDkuMjcwNSA1My44NjE3IDQ5LjUzMDggNTMuOTE0NiA0OS44MjI4IDUzLjkxNDZDNTAuMTkwOSA1My45MTQ2IDUwLjUxODkgNTMuODQwNSA1MC44MDY2IDUzLjY5MjRDNTEuMDk0NCA1My41NDQzIDUxLjM0NDEgNTMuMzM0OCA1MS41NTU3IDUzLjA2NEw1Mi4zNjE4IDUzLjg0NDdDNTIuMjEzNyA1NC4wNjA1IDUyLjAyMTIgNTQuMjY3OSA1MS43ODQyIDU0LjQ2NjhDNTEuNTQ3MiA1NC42NjE1IDUxLjI1NzMgNTQuODIwMSA1MC45MTQ2IDU0Ljk0MjlDNTAuNTc2IDU1LjA2NTYgNTAuMTgyNSA1NS4xMjcgNDkuNzMzOSA1NS4xMjdaTTU1LjQ2MTkgNDkuNDM5NVY1NUg1My45MzIxVjQ4LjEzMThINTUuMzkyMUw1NS40NjE5IDQ5LjQzOTVaTTU3LjU2MyA0OC4wODc0TDU3LjU1MDMgNDkuNTA5M0M1Ny40NTcyIDQ5LjQ5MjQgNTcuMzU1NiA0OS40Nzk3IDU3LjI0NTYgNDkuNDcxMkM1Ny4xMzk4IDQ5LjQ2MjcgNTcuMDM0IDQ5LjQ1ODUgNTYuOTI4MiA0OS40NTg1QzU2LjY2NTkgNDkuNDU4NSA1Ni40MzUyIDQ5LjQ5NjYgNTYuMjM2MyA0OS41NzI4QzU2LjAzNzQgNDkuNjQ0NyA1NS44NzAzIDQ5Ljc1MDUgNTUuNzM0OSA0OS44OTAxQzU1LjYwMzcgNTAuMDI1NiA1NS41MDIxIDUwLjE5MDYgNTUuNDMwMiA1MC4zODUzQzU1LjM1ODIgNTAuNTc5OSA1NS4zMTU5IDUwLjc5NzkgNTUuMzAzMiA1MS4wMzkxTDU0Ljk1NDEgNTEuMDY0NUM1NC45NTQxIDUwLjYzMjggNTQuOTk2NCA1MC4yMzI5IDU1LjA4MTEgNDkuODY0N0M1NS4xNjU3IDQ5LjQ5NjYgNTUuMjkyNiA0OS4xNzI5IDU1LjQ2MTkgNDguODkzNkM1NS42MzU0IDQ4LjYxNDMgNTUuODUxMiA0OC4zOTYzIDU2LjEwOTQgNDguMjM5N0M1Ni4zNzE3IDQ4LjA4MzIgNTYuNjc0MyA0OC4wMDQ5IDU3LjAxNzEgNDguMDA0OUM1Ny4xMTAyIDQ4LjAwNDkgNTcuMjA5NiA0OC4wMTMzIDU3LjMxNTQgNDguMDMwM0M1Ny40MjU1IDQ4LjA0NzIgNTcuNTA4IDQ4LjA2NjIgNTcuNTYzIDQ4LjA4NzRaTTYyLjQxNSA1My42MjI2VjUwLjM0NzJDNjIuNDE1IDUwLjEwMTcgNjIuMzcwNiA0OS44OTAxIDYyLjI4MTcgNDkuNzEyNEM2Mi4xOTI5IDQ5LjUzNDcgNjIuMDU3NSA0OS4zOTcxIDYxLjg3NTUgNDkuMjk5OEM2MS42OTc4IDQ5LjIwMjUgNjEuNDczNSA0OS4xNTM4IDYxLjIwMjYgNDkuMTUzOEM2MC45NTMgNDkuMTUzOCA2MC43MzcxIDQ5LjE5NjEgNjAuNTU1MiA0OS4yODA4QzYwLjM3MzIgNDkuMzY1NCA2MC4yMzE0IDQ5LjQ3OTcgNjAuMTI5OSA0OS42MjM1QzYwLjAyODMgNDkuNzY3NCA1OS45Nzc1IDQ5LjkzMDMgNTkuOTc3NSA1MC4xMTIzSDU4LjQ1NDFDNTguNDU0MSA0OS44NDE1IDU4LjUxOTcgNDkuNTc5MSA1OC42NTA5IDQ5LjMyNTJDNTguNzgyMSA0OS4wNzEzIDU4Ljk3MjUgNDguODQ0OSA1OS4yMjIyIDQ4LjY0NkM1OS40NzE4IDQ4LjQ0NzEgNTkuNzcwMiA0OC4yOTA1IDYwLjExNzIgNDguMTc2M0M2MC40NjQyIDQ4LjA2MiA2MC44NTM1IDQ4LjAwNDkgNjEuMjg1MiA0OC4wMDQ5QzYxLjgwMTQgNDguMDA0OSA2Mi4yNTg1IDQ4LjA5MTYgNjIuNjU2MiA0OC4yNjUxQzYzLjA1ODMgNDguNDM4NiA2My4zNzM1IDQ4LjcwMSA2My42MDIxIDQ5LjA1MjJDNjMuODM0OCA0OS4zOTkzIDYzLjk1MTIgNDkuODM1MSA2My45NTEyIDUwLjM1OTlWNTMuNDEzMUM2My45NTEyIDUzLjcyNjIgNjMuOTcyMyA1NC4wMDc2IDY0LjAxNDYgNTQuMjU3M0M2NC4wNjEyIDU0LjUwMjggNjQuMTI2OCA1NC43MTY1IDY0LjIxMTQgNTQuODk4NFY1NUg2Mi42NDM2QzYyLjU3MTYgNTQuODM1IDYyLjUxNDUgNTQuNjI1NSA2Mi40NzIyIDU0LjM3MTZDNjIuNDM0MSA1NC4xMTM0IDYyLjQxNSA1My44NjM4IDYyLjQxNSA1My42MjI2Wk02Mi42MzcyIDUwLjgyMzJMNjIuNjQ5OSA1MS43NjlINjEuNTUxOEM2MS4yNjgyIDUxLjc2OSA2MS4wMTg2IDUxLjc5NjUgNjAuODAyNyA1MS44NTE2QzYwLjU4NjkgNTEuOTAyMyA2MC40MDcxIDUxLjk3ODUgNjAuMjYzMiA1Mi4wODAxQzYwLjExOTMgNTIuMTgxNiA2MC4wMTE0IDUyLjMwNDQgNTkuOTM5NSA1Mi40NDgyQzU5Ljg2NzUgNTIuNTkyMSA1OS44MzE1IDUyLjc1NSA1OS44MzE1IDUyLjkzN0M1OS44MzE1IDUzLjExOSA1OS44NzM5IDUzLjI4NjEgNTkuOTU4NSA1My40Mzg1QzYwLjA0MzEgNTMuNTg2NiA2MC4xNjU5IDUzLjcwMyA2MC4zMjY3IDUzLjc4NzZDNjAuNDkxNyA1My44NzIyIDYwLjY5MDYgNTMuOTE0NiA2MC45MjMzIDUzLjkxNDZDNjEuMjM2NSA1My45MTQ2IDYxLjUwOTQgNTMuODUxMSA2MS43NDIyIDUzLjcyNDFDNjEuOTc5MiA1My41OTI5IDYyLjE2NTQgNTMuNDM0MiA2Mi4zMDA4IDUzLjI0OEM2Mi40MzYyIDUzLjA1NzYgNjIuNTA4MSA1Mi44Nzc4IDYyLjUxNjYgNTIuNzA4NUw2My4wMTE3IDUzLjM4NzdDNjIuOTYwOSA1My41NjEyIDYyLjg3NDIgNTMuNzQ3NCA2Mi43NTE1IDUzLjk0NjNDNjIuNjI4NyA1NC4xNDUyIDYyLjQ2NzkgNTQuMzM1NiA2Mi4yNjkgNTQuNTE3NkM2Mi4wNzQ0IDU0LjY5NTMgNjEuODM5NSA1NC44NDEzIDYxLjU2NDUgNTQuOTU1NkM2MS4yOTM2IDU1LjA2OTggNjAuOTgwNSA1NS4xMjcgNjAuNjI1IDU1LjEyN0M2MC4xNzY0IDU1LjEyNyA1OS43NzY1IDU1LjAzODEgNTkuNDI1MyA1NC44NjA0QzU5LjA3NDEgNTQuNjc4NCA1OC43OTkgNTQuNDM1MSA1OC42MDAxIDU0LjEzMDRDNTguNDAxMiA1My44MjE1IDU4LjMwMTggNTMuNDcyMyA1OC4zMDE4IDUzLjA4M0M1OC4zMDE4IDUyLjcxOTEgNTguMzY5NSA1Mi4zOTc1IDU4LjUwNDkgNTIuMTE4MkM1OC42NDQ1IDUxLjgzNDYgNTguODQ3NyA1MS41OTc3IDU5LjExNDMgNTEuNDA3MkM1OS4zODUxIDUxLjIxNjggNTkuNzE1MiA1MS4wNzI5IDYwLjEwNDUgNTAuOTc1NkM2MC40OTM4IDUwLjg3NCA2MC45MzgyIDUwLjgyMzIgNjEuNDM3NSA1MC44MjMySDYyLjYzNzJaTTY4Ljk0OTIgNDguMTMxOFY0OS4yNDlINjUuMDc3MVY0OC4xMzE4SDY4Ljk0OTJaTTY2LjE5NDMgNDYuNDQ5N0g2Ny43MjQxVjUzLjEwMjFDNjcuNzI0MSA1My4zMTM2IDY3Ljc1MzcgNTMuNDc2NiA2Ny44MTMgNTMuNTkwOEM2Ny44NzY1IDUzLjcwMDggNjcuOTYzMiA1My43NzQ5IDY4LjA3MzIgNTMuODEzQzY4LjE4MzMgNTMuODUxMSA2OC4zMTIzIDUzLjg3MDEgNjguNDYwNCA1My44NzAxQzY4LjU2NjIgNTMuODcwMSA2OC42Njc4IDUzLjg2MzggNjguNzY1MSA1My44NTExQzY4Ljg2MjUgNTMuODM4NCA2OC45NDA4IDUzLjgyNTcgNjkgNTMuODEzTDY5LjAwNjMgNTQuOTgxQzY4Ljg3OTQgNTUuMDE5IDY4LjczMTMgNTUuMDUyOSA2OC41NjIgNTUuMDgyNUM2OC4zOTcgNTUuMTEyMSA2OC4yMDY1IDU1LjEyNyA2Ny45OTA3IDU1LjEyN0M2Ny42Mzk1IDU1LjEyNyA2Ny4zMjg1IDU1LjA2NTYgNjcuMDU3NiA1NC45NDI5QzY2Ljc4NjggNTQuODE1OSA2Ni41NzUyIDU0LjYxMDcgNjYuNDIyOSA1NC4zMjcxQzY2LjI3MDUgNTQuMDQzNiA2Ni4xOTQzIDUzLjY2NyA2Ni4xOTQzIDUzLjE5NzNWNDYuNDQ5N1pNNzQuNTEyMiA1My4zODEzVjQ4LjEzMThINzYuMDQ4M1Y1NUg3NC42MDExTDc0LjUxMjIgNTMuMzgxM1pNNzQuNzI4IDUxLjk1MzFMNzUuMjQyMiA1MS45NDA0Qzc1LjI0MjIgNTIuNDAxNyA3NS4xOTE0IDUyLjgyNyA3NS4wODk4IDUzLjIxNjNDNzQuOTg4MyA1My42MDE0IDc0LjgzMTcgNTMuOTM3OCA3NC42MjAxIDU0LjIyNTZDNzQuNDA4NSA1NC41MDkxIDc0LjEzNzcgNTQuNzMxMyA3My44MDc2IDU0Ljg5MjFDNzMuNDc3NSA1NS4wNDg3IDczLjA4MTkgNTUuMTI3IDcyLjYyMDYgNTUuMTI3QzcyLjI4NjMgNTUuMTI3IDcxLjk3OTUgNTUuMDc4MyA3MS43MDAyIDU0Ljk4MUM3MS40MjA5IDU0Ljg4MzYgNzEuMTc5NyA1NC43MzM0IDcwLjk3NjYgNTQuNTMwM0M3MC43Nzc3IDU0LjMyNzEgNzAuNjIzMiA1NC4wNjI3IDcwLjUxMzIgNTMuNzM2OEM3MC40MDMyIDUzLjQxMSA3MC4zNDgxIDUzLjAyMTYgNzAuMzQ4MSA1Mi41Njg4VjQ4LjEzMThINzEuODc3OVY1Mi41ODE1QzcxLjg3NzkgNTIuODMxMiA3MS45MDc2IDUzLjA0MDcgNzEuOTY2OCA1My4yMUM3Mi4wMjYgNTMuMzc1IDcyLjEwNjQgNTMuNTA4MyA3Mi4yMDggNTMuNjA5OUM3Mi4zMDk2IDUzLjcxMTQgNzIuNDI4MSA1My43ODM0IDcyLjU2MzUgNTMuODI1N0M3Mi42OTg5IDUzLjg2OCA3Mi44NDI4IDUzLjg4OTIgNzIuOTk1MSA1My44ODkyQzczLjQzMSA1My44ODkyIDczLjc3MzggNTMuODA0NSA3NC4wMjM0IDUzLjYzNTNDNzQuMjc3MyA1My40NjE4IDc0LjQ1NzIgNTMuMjI5IDc0LjU2MyA1Mi45MzdDNzQuNjczIDUyLjY0NSA3NC43MjggNTIuMzE3MSA3NC43MjggNTEuOTUzMVpNNzkuNDAyMyA0OS40Mzk1VjU1SDc3Ljg3MjZWNDguMTMxOEg3OS4zMzI1TDc5LjQwMjMgNDkuNDM5NVpNODEuNTAzNCA0OC4wODc0TDgxLjQ5MDcgNDkuNTA5M0M4MS4zOTc2IDQ5LjQ5MjQgODEuMjk2MSA0OS40Nzk3IDgxLjE4NiA0OS40NzEyQzgxLjA4MDIgNDkuNDYyNyA4MC45NzQ0IDQ5LjQ1ODUgODAuODY4NyA0OS40NTg1QzgwLjYwNjMgNDkuNDU4NSA4MC4zNzU3IDQ5LjQ5NjYgODAuMTc2OCA0OS41NzI4Qzc5Ljk3NzkgNDkuNjQ0NyA3OS44MTA3IDQ5Ljc1MDUgNzkuNjc1MyA0OS44OTAxQzc5LjU0NDEgNTAuMDI1NiA3OS40NDI1IDUwLjE5MDYgNzkuMzcwNiA1MC4zODUzQzc5LjI5ODcgNTAuNTc5OSA3OS4yNTYzIDUwLjc5NzkgNzkuMjQzNyA1MS4wMzkxTDc4Ljg5NDUgNTEuMDY0NUM3OC44OTQ1IDUwLjYzMjggNzguOTM2OCA1MC4yMzI5IDc5LjAyMTUgNDkuODY0N0M3OS4xMDYxIDQ5LjQ5NjYgNzkuMjMzMSA0OS4xNzI5IDc5LjQwMjMgNDguODkzNkM3OS41NzU4IDQ4LjYxNDMgNzkuNzkxNyA0OC4zOTYzIDgwLjA0OTggNDguMjM5N0M4MC4zMTIyIDQ4LjA4MzIgODAuNjE0NyA0OC4wMDQ5IDgwLjk1NzUgNDguMDA0OUM4MS4wNTA2IDQ4LjAwNDkgODEuMTUwMSA0OC4wMTMzIDgxLjI1NTkgNDguMDMwM0M4MS4zNjU5IDQ4LjA0NzIgODEuNDQ4NCA0OC4wNjYyIDgxLjUwMzQgNDguMDg3NFpNODUuNjEyOCA1NS4xMjdDODUuMTA1IDU1LjEyNyA4NC42NDU4IDU1LjA0NDQgODQuMjM1NCA1NC44Nzk0QzgzLjgyOTEgNTQuNzEwMSA4My40ODIxIDU0LjQ3NTMgODMuMTk0MyA1NC4xNzQ4QzgyLjkxMDggNTMuODc0MyA4Mi42OTI5IDUzLjUyMSA4Mi41NDA1IDUzLjExNDdDODIuMzg4MiA1Mi43MDg1IDgyLjMxMiA1Mi4yNzA1IDgyLjMxMiA1MS44MDA4VjUxLjU0NjlDODIuMzEyIDUxLjAwOTQgODIuMzkwMyA1MC41MjI4IDgyLjU0NjkgNTAuMDg2OUM4Mi43MDM1IDQ5LjY1MSA4Mi45MjE0IDQ5LjI3ODYgODMuMjAwNyA0OC45Njk3QzgzLjQ4IDQ4LjY1NjYgODMuODEwMSA0OC40MTc1IDg0LjE5MDkgNDguMjUyNEM4NC41NzE4IDQ4LjA4NzQgODQuOTg0NCA0OC4wMDQ5IDg1LjQyODcgNDguMDA0OUM4NS45MTk2IDQ4LjAwNDkgODYuMzQ5MSA0OC4wODc0IDg2LjcxNzMgNDguMjUyNEM4Ny4wODU0IDQ4LjQxNzUgODcuMzkwMSA0OC42NTAyIDg3LjYzMTMgNDguOTUwN0M4Ny44NzY4IDQ5LjI0NjkgODguMDU4OCA0OS42MDAzIDg4LjE3NzIgNTAuMDEwN0M4OC4zIDUwLjQyMTIgODguMzYxMyA1MC44NzQgODguMzYxMyA1MS4zNjkxVjUyLjAyMjlIODMuMDU0N1Y1MC45MjQ4SDg2Ljg1MDZWNTAuODA0MkM4Ni44NDIxIDUwLjUyOTEgODYuNzg3MSA1MC4yNzEgODYuNjg1NSA1MC4wMjk4Qzg2LjU4ODIgNDkuNzg4NiA4Ni40MzggNDkuNTkzOSA4Ni4yMzQ5IDQ5LjQ0NThDODYuMDMxNyA0OS4yOTc3IDg1Ljc2MDkgNDkuMjIzNiA4NS40MjI0IDQ5LjIyMzZDODUuMTY4NSA0OS4yMjM2IDg0Ljk0MjEgNDkuMjc4NiA4NC43NDMyIDQ5LjM4ODdDODQuNTQ4NSA0OS40OTQ1IDg0LjM4NTYgNDkuNjQ4OSA4NC4yNTQ0IDQ5Ljg1MjFDODQuMTIzMiA1MC4wNTUyIDg0LjAyMTYgNTAuMzAwNiA4My45NDk3IDUwLjU4ODRDODMuODgyIDUwLjg3MTkgODMuODQ4MSA1MS4xOTE0IDgzLjg0ODEgNTEuNTQ2OVY1MS44MDA4QzgzLjg0ODEgNTIuMTAxMiA4My44ODgzIDUyLjM4MDUgODMuOTY4OCA1Mi42Mzg3Qzg0LjA1MzQgNTIuODkyNiA4NC4xNzYxIDUzLjExNDcgODQuMzM2OSA1My4zMDUyQzg0LjQ5NzcgNTMuNDk1NiA4NC42OTI0IDUzLjY0NTggODQuOTIwOSA1My43NTU5Qzg1LjE0OTQgNTMuODYxNyA4NS40MDk3IDUzLjkxNDYgODUuNzAxNyA1My45MTQ2Qzg2LjA2OTggNTMuOTE0NiA4Ni4zOTc4IDUzLjg0MDUgODYuNjg1NSA1My42OTI0Qzg2Ljk3MzMgNTMuNTQ0MyA4Ny4yMjMgNTMuMzM0OCA4Ny40MzQ2IDUzLjA2NEw4OC4yNDA3IDUzLjg0NDdDODguMDkyNiA1NC4wNjA1IDg3LjkwMDEgNTQuMjY3OSA4Ny42NjMxIDU0LjQ2NjhDODcuNDI2MSA1NC42NjE1IDg3LjEzNjIgNTQuODIwMSA4Ni43OTM1IDU0Ljk0MjlDODYuNDU0OSA1NS4wNjU2IDg2LjA2MTQgNTUuMTI3IDg1LjYxMjggNTUuMTI3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjMuMTYzNiAxMDguMDI2VjExMEgxMy4xMzA4VjEwOC4zMDRMMTguMDAzMyAxMDIuOTg5QzE4LjUzNzggMTAyLjM4NiAxOC45NTkzIDEwMS44NjUgMTkuMjY3NyAxMDEuNDI3QzE5LjU3NjEgMTAwLjk4OCAxOS43OTE5IDEwMC41OTQgMTkuOTE1MyAxMDAuMjQ1QzIwLjA0NTUgOTkuODg4MyAyMC4xMTA2IDk5LjU0MjIgMjAuMTEwNiA5OS4yMDY0QzIwLjExMDYgOTguNzMzNiAyMC4wMjE1IDk4LjMxODkgMTkuODQzMyA5Ny45NjI2QzE5LjY3MiA5Ny41OTk0IDE5LjQxODQgOTcuMzE1IDE5LjA4MjYgOTcuMTA5NEMxOC43NDY4IDk2Ljg5NjkgMTguMzM5MSA5Ni43OTA3IDE3Ljg1OTQgOTYuNzkwN0MxNy4zMDQzIDk2Ljc5MDcgMTYuODM4MyA5Ni45MTA2IDE2LjQ2MTMgOTcuMTUwNUMxNi4wODQ0IDk3LjM5MDQgMTUuOCA5Ny43MjI3IDE1LjYwODEgOTguMTQ3NkMxNS40MTYzIDk4LjU2NTcgMTUuMzIwMyA5OS4wNDU0IDE1LjMyMDMgOTkuNTg2OEgxMi44NDI5QzEyLjg0MjkgOTguNzE2NCAxMy4wNDE3IDk3LjkyMTUgMTMuNDM5MSA5Ny4yMDE5QzEzLjgzNjYgOTYuNDc1NSAxNC40MTIzIDk1Ljg5OTggMTUuMTY2MSA5NS40NzQ5QzE1LjkyIDk1LjA0MzIgMTYuODI4IDk0LjgyNzMgMTcuODkwMiA5NC44MjczQzE4Ljg5MDggOTQuODI3MyAxOS43NDA1IDk0Ljk5NTIgMjAuNDM5NiA5NS4zMzFDMjEuMTM4NiA5NS42NjY4IDIxLjY2OTcgOTYuMTQzMSAyMi4wMzI5IDk2Ljc1OTlDMjIuNDAzIDk3LjM3NjcgMjIuNTg4IDk4LjEwNjUgMjIuNTg4IDk4Ljk0OTRDMjIuNTg4IDk5LjQxNTQgMjIuNTEyNiA5OS44NzggMjIuMzYxOCAxMDAuMzM3QzIyLjIxMTEgMTAwLjc5NiAyMS45OTUyIDEwMS4yNTUgMjEuNzE0MiAxMDEuNzE1QzIxLjQ0MDEgMTAyLjE2NyAyMS4xMTQ2IDEwMi42MjMgMjAuNzM3NyAxMDMuMDgyQzIwLjM2MDcgMTAzLjUzNCAxOS45NDYxIDEwMy45OTMgMTkuNDkzOCAxMDQuNDU5TDE2LjI1NTggMTA4LjAyNkgyMy4xNjM2Wk0zNS4xMjkxIDEwOC4wMjZWMTEwSDI1LjA5NjJWMTA4LjMwNEwyOS45Njg3IDEwMi45ODlDMzAuNTAzMyAxMDIuMzg2IDMwLjkyNDcgMTAxLjg2NSAzMS4yMzMxIDEwMS40MjdDMzEuNTQxNSAxMDAuOTg4IDMxLjc1NzQgMTAwLjU5NCAzMS44ODA3IDEwMC4yNDVDMzIuMDExIDk5Ljg4ODMgMzIuMDc2MSA5OS41NDIyIDMyLjA3NjEgOTkuMjA2NEMzMi4wNzYxIDk4LjczMzYgMzEuOTg3IDk4LjMxODkgMzEuODA4OCA5Ny45NjI2QzMxLjYzNzUgOTcuNTk5NCAzMS4zODM5IDk3LjMxNSAzMS4wNDgxIDk3LjEwOTRDMzAuNzEyMyA5Ni44OTY5IDMwLjMwNDUgOTYuNzkwNyAyOS44MjQ4IDk2Ljc5MDdDMjkuMjY5NyA5Ni43OTA3IDI4LjgwMzcgOTYuOTEwNiAyOC40MjY4IDk3LjE1MDVDMjguMDQ5OSA5Ny4zOTA0IDI3Ljc2NTUgOTcuNzIyNyAyNy41NzM2IDk4LjE0NzZDMjcuMzgxNyA5OC41NjU3IDI3LjI4NTggOTkuMDQ1NCAyNy4yODU4IDk5LjU4NjhIMjQuODA4NEMyNC44MDg0IDk4LjcxNjQgMjUuMDA3MSA5Ny45MjE1IDI1LjQwNDYgOTcuMjAxOUMyNS44MDIxIDk2LjQ3NTUgMjYuMzc3NyA5NS44OTk4IDI3LjEzMTYgOTUuNDc0OUMyNy44ODU0IDk1LjA0MzIgMjguNzkzNCA5NC44MjczIDI5Ljg1NTcgOTQuODI3M0MzMC44NTYyIDk0LjgyNzMgMzEuNzA2IDk0Ljk5NTIgMzIuNDA1IDk1LjMzMUMzMy4xMDQgOTUuNjY2OCAzMy42MzUxIDk2LjE0MzEgMzMuOTk4MyA5Ni43NTk5QzM0LjM2ODQgOTcuMzc2NyAzNC41NTM0IDk4LjEwNjUgMzQuNTUzNCA5OC45NDk0QzM0LjU1MzQgOTkuNDE1NCAzNC40NzgxIDk5Ljg3OCAzNC4zMjczIDEwMC4zMzdDMzQuMTc2NSAxMDAuNzk2IDMzLjk2MDYgMTAxLjI1NSAzMy42Nzk3IDEwMS43MTVDMzMuNDA1NiAxMDIuMTY3IDMzLjA4IDEwMi42MjMgMzIuNzAzMSAxMDMuMDgyQzMyLjMyNjIgMTAzLjUzNCAzMS45MTE2IDEwMy45OTMgMzEuNDU5MyAxMDQuNDU5TDI4LjIyMTIgMTA4LjAyNkgzNS4xMjkxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuMzI2MSA5Ny41NzJDNDEuMzI2MSA5Ny4wNzE3IDQxLjQ0OTQgOTYuNjEyNSA0MS42OTYxIDk2LjE5NDVDNDEuOTQyOCA5NS43NzY1IDQyLjI3MTggOTUuNDQ0MSA0Mi42ODMgOTUuMTk3NEM0My4xMDEgOTQuOTQzOCA0My41NTMzIDk0LjgxNyA0NC4wMzk5IDk0LjgxN0M0NC41MzMzIDk0LjgxNyA0NC45ODIyIDk0Ljk0MzggNDUuMzg2NSA5NS4xOTc0QzQ1Ljc5MDggOTUuNDQ0MSA0Ni4xMTI5IDk1Ljc3NjUgNDYuMzUyOCA5Ni4xOTQ1QzQ2LjU5OTUgOTYuNjEyNSA0Ni43MjI5IDk3LjA3MTcgNDYuNzIyOSA5Ny41NzJDNDYuNzIyOSA5OC4wNzIyIDQ2LjU5OTUgOTguNTMxNCA0Ni4zNTI4IDk4Ljk0OTRDNDYuMTEyOSA5OS4zNjA2IDQ1Ljc5MDggOTkuNjg2MSA0NS4zODY1IDk5LjkyNkM0NC45ODIyIDEwMC4xNjYgNDQuNTMzMyAxMDAuMjg2IDQ0LjAzOTkgMTAwLjI4NkM0My41NTMzIDEwMC4yODYgNDMuMTAxIDEwMC4xNjYgNDIuNjgzIDk5LjkyNkM0Mi4yNzE4IDk5LjY4NjEgNDEuOTQyOCA5OS4zNjA2IDQxLjY5NjEgOTguOTQ5NEM0MS40NDk0IDk4LjUzMTQgNDEuMzI2MSA5OC4wNzIyIDQxLjMyNjEgOTcuNTcyWk00Mi43MTM4IDk3LjU3MkM0Mi43MTM4IDk3Ljk0MiA0Mi44NDQgOTguMjUzOCA0My4xMDQ0IDk4LjUwNzRDNDMuMzY0OSA5OC43NTQxIDQzLjY3NjcgOTguODc3NSA0NC4wMzk5IDk4Ljg3NzVDNDQuNDAzMSA5OC44Nzc1IDQ0LjcwODEgOTguNzU0MSA0NC45NTQ4IDk4LjUwNzRDNDUuMjAxNSA5OC4yNjA3IDQ1LjMyNDggOTcuOTQ4OSA0NS4zMjQ4IDk3LjU3MkM0NS4zMjQ4IDk3LjE4ODIgNDUuMjAxNSA5Ni44Njk1IDQ0Ljk1NDggOTYuNjE2QzQ0LjcwODEgOTYuMzYyNCA0NC40MDMxIDk2LjIzNTYgNDQuMDM5OSA5Ni4yMzU2QzQzLjY3NjcgOTYuMjM1NiA0My4zNjQ5IDk2LjM2MjQgNDMuMTA0NCA5Ni42MTZDNDIuODQ0IDk2Ljg2OTUgNDIuNzEzOCA5Ny4xODgyIDQyLjcxMzggOTcuNTcyWk01OC40MjEgMTA1LjEyN0g2MC45OTA5QzYwLjkwODcgMTA2LjEwNyA2MC42MzQ2IDEwNi45ODEgNjAuMTY4NiAxMDcuNzQ5QzU5LjcwMjYgMTA4LjUwOSA1OS4wNDgxIDEwOS4xMDkgNTguMjA1MiAxMDkuNTQ4QzU3LjM2MjIgMTA5Ljk4NiA1Ni4zMzc3IDExMC4yMDYgNTUuMTMxNiAxMTAuMjA2QzU0LjIwNjQgMTEwLjIwNiA1My4zNzM4IDExMC4wNDEgNTIuNjMzNiAxMDkuNzEyQzUxLjg5MzUgMTA5LjM3NiA1MS4yNTk2IDEwOC45MDQgNTAuNzMxOSAxMDguMjk0QzUwLjIwNDIgMTA3LjY3NyA0OS43OTk5IDEwNi45MzMgNDkuNTE4OSAxMDYuMDYzQzQ5LjI0NDggMTA1LjE5MyA0OS4xMDc3IDEwNC4yMTkgNDkuMTA3NyAxMDMuMTQ0VjEwMS45QzQ5LjEwNzcgMTAwLjgyNCA0OS4yNDgyIDk5Ljg1MDYgNDkuNTI5MiA5OC45ODAzQzQ5LjgxNyA5OC4xMDk5IDUwLjIyODIgOTcuMzY2NCA1MC43NjI3IDk2Ljc0OTZDNTEuMjk3MyA5Ni4xMjYgNTEuOTM4IDk1LjY0OTcgNTIuNjg1IDk1LjMyMDdDNTMuNDM4OSA5NC45OTE4IDU0LjI4NTIgOTQuODI3MyA1NS4yMjQxIDk0LjgyNzNDNTYuNDE2NSA5NC44MjczIDU3LjQyMzkgOTUuMDQ2NiA1OC4yNDYzIDk1LjQ4NTJDNTkuMDY4NyA5NS45MjM4IDU5LjcwNiA5Ni41MzAzIDYwLjE1ODMgOTcuMzA0N0M2MC42MTc0IDk4LjA3OTEgNjAuODk4NCA5OC45NjY2IDYxLjAwMTIgOTkuOTY3MUg1OC40MzEzQzU4LjM2MjggOTkuMzIyOSA1OC4yMTIgOTguNzcxMyA1Ny45NzkgOTguMzEyMUM1Ny43NTI5IDk3Ljg1MjkgNTcuNDE3MSA5Ny41MDM0IDU2Ljk3MTYgOTcuMjYzNkM1Ni41MjYyIDk3LjAxNjkgNTUuOTQzNyA5Ni44OTM1IDU1LjIyNDEgOTYuODkzNUM1NC42MzQ3IDk2Ljg5MzUgNTQuMTIwNyA5Ny4wMDMyIDUzLjY4MjEgOTcuMjIyNUM1My4yNDM1IDk3LjQ0MTggNTIuODc2OSA5Ny43NjM5IDUyLjU4MjIgOTguMTg4N0M1Mi4yODc1IDk4LjYxMzYgNTIuMDY0OCA5OS4xMzc5IDUxLjkxNDEgOTkuNzYxNUM1MS43NzAxIDEwMC4zNzggNTEuNjk4MiAxMDEuMDg0IDUxLjY5ODIgMTAxLjg3OVYxMDMuMTQ0QzUxLjY5ODIgMTAzLjg5NyA1MS43NjMzIDEwNC41ODMgNTEuODkzNSAxMDUuMTk5QzUyLjAzMDYgMTA1LjgwOSA1Mi4yMzYxIDEwNi4zMzQgNTIuNTEwMyAxMDYuNzcyQzUyLjc5MTIgMTA3LjIxMSA1My4xNDc2IDEwNy41NSA1My41NzkzIDEwNy43OUM1NC4wMTExIDEwOC4wMyA1NC41Mjg1IDEwOC4xNSA1NS4xMzE2IDEwOC4xNUM1NS44NjQ4IDEwOC4xNSA1Ni40NTc2IDEwOC4wMzMgNTYuOTA5OSAxMDcuOEM1Ny4zNjkxIDEwNy41NjcgNTcuNzE1MiAxMDcuMjI4IDU3Ljk0ODIgMTA2Ljc4MkM1OC4xODggMTA2LjMzIDU4LjM0NTcgMTA1Ljc3OSA1OC40MjEgMTA1LjEyN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY4Ljc4MiAxMTEuMTQ4TDc5LjA3MzIgMTA0Ljg3NkM4MC43NjQ3IDEwMy44NDUgODIuMDcgMTAyLjI4NiA4Mi43ODc1IDEwMC40NEw4NS44OTA4IDkyLjQ1NDVDODguMjQ5NSA4Ni4zODQ5IDk2LjgzNzkgODYuMzg0OSA5OS4xOTY3IDkyLjQ1NDVWOTIuNDU0NUMxMDEuMjczIDk3Ljc5NzkgMTA4LjQ4MyA5OC42MzY4IDExMS43MzEgOTMuOTEzTDExNS40MDMgODguNTcyNUMxMTcuNjY3IDg1LjI3OTIgMTIyLjMyOCA4NC43NjUgMTI1LjI1NiA4Ny40ODU2Vjg3LjQ4NTZDMTI4LjUzMyA5MC41MzA0IDEzMy44MyA4OS40NjY1IDEzNS42NzcgODUuMzkyN0wxMzguNTAxIDc5LjE2MzFDMTQxLjYxMiA3Mi4zMDIxIDE1MS4yNjQgNzIuMDQ5MSAxNTQuNzMgNzguNzM3OUwxNjAuMTcgODkuMjM3QzE2Mi4yNjEgOTMuMjcyOCAxNjcuMDI4IDk1LjEyMjkgMTcxLjI5NCA5My41NTQ2TDE3NC42MTIgOTIuMzM0OEMxNzYuODM4IDkxLjUxNjYgMTc5LjI5NyA5MS42MDc1IDE4MS40NTYgOTIuNTg4MUwxODkuNjI3IDk2LjI5ODgiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUwMzc2IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+Cg==", "description": "Displays a single entity historical telemetry values as a simplified chart. Optionally may display the corresponding latest telemetry value.", "descriptor": { "type": "timeseries", @@ -22,6 +22,5 @@ "basicModeDirective": "tb-value-chart-card-basic-config", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgb(63, 82, 221)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgb(63, 82, 221)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Simple Value and chart card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" }, - "externalId": null, "tags": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/single_switch.json b/application/src/main/data/json/system/widget_types/single_switch.json index 7e5440b41d..8faea8ba93 100644 --- a/application/src/main/data/json/system/widget_types/single_switch.json +++ b/application/src/main/data/json/system/widget_types/single_switch.json @@ -2,8 +2,8 @@ "fqn": "single_switch", "name": "Single Switch", "deprecated": false, - "image": "tb-image:c2luZ2xlLXN3aXRjaC5zdmc=:IlNpbmdsZSBTd2l0Y2giIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAxIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMSAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8zNjM5XzE1NTY3NCkiPgo8cmVjdCB4PSIwLjUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMSIgeT0iMC41IiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgcng9IjMuNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHJlY3QgeD0iNTUiIHk9IjcwIiB3aWR0aD0iMzguMDk1MiIgaGVpZ2h0PSIxOS45NTQ3IiByeD0iOS45NzczMyIgZmlsbD0iIzU0NjlGRiIvPgo8Y2lyY2xlIGN4PSI4My4xMTY0IiBjeT0iNzkuOTc3MiIgcj0iOC4xNjMyNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEwNi42OTQgODEuNzUzNEMxMDYuNjk0IDgxLjUzMzcgMTA2LjY2IDgxLjMzODQgMTA2LjU5MiA4MS4xNjc1QzEwNi41MjggODAuOTk2NiAxMDYuNDE0IDgwLjg0MDMgMTA2LjI0OCA4MC42OTg3QzEwNi4wODIgODAuNTU3MSAxMDUuODQ3IDgwLjQyMDQgMTA1LjU0NCA4MC4yODg2QzEwNS4yNDcgODAuMTUxOSAxMDQuODY2IDgwLjAxMjcgMTA0LjQwMiA3OS44NzExQzEwMy44OTQgNzkuNzE0OCAxMDMuNDI1IDc5LjU0MTUgMTAyLjk5NiA3OS4zNTExQzEwMi41NzEgNzkuMTU1OCAxMDIuMiA3OC45MzEyIDEwMS44ODIgNzguNjc3MkMxMDEuNTY1IDc4LjQxODUgMTAxLjMxOCA3OC4xMjMgMTAxLjE0MyA3Ny43OTFDMTAwLjk2NyA3Ny40NTQxIDEwMC44NzkgNzcuMDY1OSAxMDAuODc5IDc2LjYyNjVDMTAwLjg3OSA3Ni4xOTE5IDEwMC45NjkgNzUuNzk2NCAxMDEuMTUgNzUuNDM5OUMxMDEuMzM1IDc1LjA4MzUgMTAxLjU5NyA3NC43NzU5IDEwMS45MzQgNzQuNTE3MUMxMDIuMjc1IDc0LjI1MzQgMTAyLjY3OCA3NC4wNTA4IDEwMy4xNDIgNzMuOTA5MkMxMDMuNjA2IDczLjc2MjcgMTA0LjExOSA3My42ODk1IDEwNC42OCA3My42ODk1QzEwNS40NzEgNzMuNjg5NSAxMDYuMTUyIDczLjgzNTkgMTA2LjcyNCA3NC4xMjg5QzEwNy4zIDc0LjQyMTkgMTA3Ljc0MiA3NC44MTQ5IDEwOC4wNDkgNzUuMzA4MUMxMDguMzYyIDc1LjgwMTMgMTA4LjUxOCA3Ni4zNDU3IDEwOC41MTggNzYuOTQxNEgxMDYuNjk0QzEwNi42OTQgNzYuNTg5OCAxMDYuNjE5IDc2LjI3OTggMTA2LjQ2NyA3Ni4wMTEyQzEwNi4zMjEgNzUuNzM3OCAxMDYuMDk2IDc1LjUyMjkgMTA1Ljc5MyA3NS4zNjY3QzEwNS40OTYgNzUuMjEwNCAxMDUuMTE3IDc1LjEzMjMgMTA0LjY1OCA3NS4xMzIzQzEwNC4yMjQgNzUuMTMyMyAxMDMuODYyIDc1LjE5ODIgMTAzLjU3NCA3NS4zMzAxQzEwMy4yODYgNzUuNDYxOSAxMDMuMDcxIDc1LjY0MDEgMTAyLjkzIDc1Ljg2NDdDMTAyLjc4OCA3Ni4wODk0IDEwMi43MTcgNzYuMzQzMyAxMDIuNzE3IDc2LjYyNjVDMTAyLjcxNyA3Ni44MjY3IDEwMi43NjQgNzcuMDA5OCAxMDIuODU2IDc3LjE3NThDMTAyLjk0OSA3Ny4zMzY5IDEwMy4wOTEgNzcuNDg4MyAxMDMuMjgxIDc3LjYyOTlDMTAzLjQ3MiA3Ny43NjY2IDEwMy43MTEgNzcuODk2IDEwMy45OTkgNzguMDE4MUMxMDQuMjg3IDc4LjE0MDEgMTA0LjYyNiA3OC4yNTczIDEwNS4wMTcgNzguMzY5NkMxMDUuNjA4IDc4LjU0NTQgMTA2LjEyMyA3OC43NDA3IDEwNi41NjIgNzguOTU1NkMxMDcuMDAyIDc5LjE2NTUgMTA3LjM2OCA3OS40MDQ4IDEwNy42NjEgNzkuNjczM0MxMDcuOTU0IDc5Ljk0MTkgMTA4LjE3NCA4MC4yNDcxIDEwOC4zMiA4MC41ODg5QzEwOC40NjcgODAuOTI1OCAxMDguNTQgODEuMzA5MSAxMDguNTQgODEuNzM4OEMxMDguNTQgODIuMTg4IDEwOC40NSA4Mi41OTMzIDEwOC4yNjkgODIuOTU0NkMxMDguMDg4IDgzLjMxMSAxMDcuODMgODMuNjE2MiAxMDcuNDkzIDgzLjg3MDFDMTA3LjE2MSA4NC4xMTkxIDEwNi43NiA4NC4zMTIgMTA2LjI5MiA4NC40NDg3QzEwNS44MjggODQuNTgwNiAxMDUuMzEgODQuNjQ2NSAxMDQuNzM5IDg0LjY0NjVDMTA0LjIyNiA4NC42NDY1IDEwMy43MjEgODQuNTc4MSAxMDMuMjIzIDg0LjQ0MTRDMTAyLjcyOSA4NC4zMDQ3IDEwMi4yOCA4NC4wOTcyIDEwMS44NzUgODMuODE4OEMxMDEuNDcgODMuNTM1NiAxMDEuMTQ3IDgzLjE4NDEgMTAwLjkwOCA4Mi43NjQyQzEwMC42NjkgODIuMzM5NCAxMDAuNTQ5IDgxLjg0MzggMTAwLjU0OSA4MS4yNzczSDEwMi4zODhDMTAyLjM4OCA4MS42MjQgMTAyLjQ0NiA4MS45MTk0IDEwMi41NjMgODIuMTYzNkMxMDIuNjg2IDgyLjQwNzcgMTAyLjg1NCA4Mi42MDc5IDEwMy4wNjkgODIuNzY0MkMxMDMuMjg0IDgyLjkxNTUgMTAzLjUzMyA4My4wMjc4IDEwMy44MTYgODMuMTAxMUMxMDQuMTA0IDgzLjE3NDMgMTA0LjQxMiA4My4yMTA5IDEwNC43MzkgODMuMjEwOUMxMDUuMTY4IDgzLjIxMDkgMTA1LjUyNyA4My4xNDk5IDEwNS44MTUgODMuMDI3OEMxMDYuMTA4IDgyLjkwNTggMTA2LjMyOCA4Mi43MzQ5IDEwNi40NzUgODIuNTE1MUMxMDYuNjIxIDgyLjI5NTQgMTA2LjY5NCA4Mi4wNDE1IDEwNi42OTQgODEuNzUzNFpNMTEyLjE0NCA4Mi43NDIyTDExMy45NzUgNzYuNTc1MkgxMTUuMTAzTDExNC43OTUgNzguNDIwOUwxMTIuOTQ5IDg0LjVIMTExLjkzOEwxMTIuMTQ0IDgyLjc0MjJaTTExMS4wNjcgNzYuNTc1MkwxMTIuNDk1IDgyLjc3MTVMMTEyLjYxMiA4NC41SDExMS40ODRMMTA5LjMzOCA3Ni41NzUySDExMS4wNjdaTTExNi44MTYgODIuNjk4MkwxMTguMjAxIDc2LjU3NTJIMTE5LjkyMkwxMTcuNzgzIDg0LjVIMTE2LjY1NUwxMTYuODE2IDgyLjY5ODJaTTExNS4yOTMgNzYuNTc1MkwxMTcuMTAyIDgyLjY2ODlMMTE3LjMyOSA4NC41SDExNi4zMThMMTE0LjQ1MSA3OC40MTM2TDExNC4xNDMgNzYuNTc1MkgxMTUuMjkzWk0xMjMuMDEzIDc2LjU3NTJWODQuNUgxMjEuMjRWNzYuNTc1MkgxMjMuMDEzWk0xMjEuMTIzIDc0LjQ5NTFDMTIxLjEyMyA3NC4yMjY2IDEyMS4yMTEgNzQuMDA0NCAxMjEuMzg3IDczLjgyODZDMTIxLjU2NyA3My42NDc5IDEyMS44MTYgNzMuNTU3NiAxMjIuMTM0IDczLjU1NzZDMTIyLjQ0NiA3My41NTc2IDEyMi42OTMgNzMuNjQ3OSAxMjIuODc0IDczLjgyODZDMTIzLjA1NCA3NC4wMDQ0IDEyMy4xNDUgNzQuMjI2NiAxMjMuMTQ1IDc0LjQ5NTFDMTIzLjE0NSA3NC43NTg4IDEyMy4wNTQgNzQuOTc4NSAxMjIuODc0IDc1LjE1NDNDMTIyLjY5MyA3NS4zMzAxIDEyMi40NDYgNzUuNDE4IDEyMi4xMzQgNzUuNDE4QzEyMS44MTYgNzUuNDE4IDEyMS41NjcgNzUuMzMwMSAxMjEuMzg3IDc1LjE1NDNDMTIxLjIxMSA3NC45Nzg1IDEyMS4xMjMgNzQuNzU4OCAxMjEuMTIzIDc0LjQ5NTFaTTEyOC41NzkgNzYuNTc1MlY3Ny44NjQzSDEyNC4xMTFWNzYuNTc1MkgxMjguNTc5Wk0xMjUuNCA3NC42MzQzSDEyNy4xNjZWODIuMzEwMUMxMjcuMTY2IDgyLjU1NDIgMTI3LjIgODIuNzQyMiAxMjcuMjY4IDgyLjg3NEMxMjcuMzQxIDgzLjAwMSAxMjcuNDQxIDgzLjA4NjQgMTI3LjU2OCA4My4xMzA0QzEyNy42OTUgODMuMTc0MyAxMjcuODQ0IDgzLjE5NjMgMTI4LjAxNSA4My4xOTYzQzEyOC4xMzcgODMuMTk2MyAxMjguMjU0IDgzLjE4OSAxMjguMzY3IDgzLjE3NDNDMTI4LjQ3OSA4My4xNTk3IDEyOC41NjkgODMuMTQ1IDEyOC42MzggODMuMTMwNEwxMjguNjQ1IDg0LjQ3OEMxMjguNDk5IDg0LjUyMiAxMjguMzI4IDg0LjU2MSAxMjguMTMyIDg0LjU5NTJDMTI3Ljk0MiA4NC42Mjk0IDEyNy43MjIgODQuNjQ2NSAxMjcuNDczIDg0LjY0NjVDMTI3LjA2OCA4NC42NDY1IDEyNi43MDkgODQuNTc1NyAxMjYuMzk2IDg0LjQzNDFDMTI2LjA4NCA4NC4yODc2IDEyNS44NCA4NC4wNTA4IDEyNS42NjQgODMuNzIzNkMxMjUuNDg4IDgzLjM5NjUgMTI1LjQgODIuOTYxOSAxMjUuNCA4Mi40MTk5Vjc0LjYzNDNaTTEzMy4xNzkgODMuMjQwMkMxMzMuNDY3IDgzLjI0MDIgMTMzLjcyNiA4My4xODQxIDEzMy45NTUgODMuMDcxOEMxMzQuMTg5IDgyLjk1NDYgMTM0LjM3NyA4Mi43OTM1IDEzNC41MTkgODIuNTg4NEMxMzQuNjY2IDgyLjM4MzMgMTM0Ljc0NiA4Mi4xNDY1IDEzNC43NjEgODEuODc3OUgxMzYuNDIzQzEzNi40MTQgODIuMzkwNiAxMzYuMjYyIDgyLjg1NjkgMTM1Ljk2OSA4My4yNzY5QzEzNS42NzYgODMuNjk2OCAxMzUuMjg4IDg0LjAzMTIgMTM0LjgwNSA4NC4yODAzQzEzNC4zMjEgODQuNTI0NCAxMzMuNzg3IDg0LjY0NjUgMTMzLjIwMSA4NC42NDY1QzEzMi41OTUgODQuNjQ2NSAxMzIuMDY4IDg0LjU0MzkgMTMxLjYxOSA4NC4zMzg5QzEzMS4xNjkgODQuMTI4OSAxMzAuNzk2IDgzLjg0MDggMTMwLjQ5OCA4My40NzQ2QzEzMC4yIDgzLjEwODQgMTI5Ljk3NiA4Mi42ODYgMTI5LjgyNCA4Mi4yMDc1QzEyOS42NzggODEuNzI5IDEyOS42MDQgODEuMjE2MyAxMjkuNjA0IDgwLjY2OTRWODAuNDEzMUMxMjkuNjA0IDc5Ljg2NjIgMTI5LjY3OCA3OS4zNTM1IDEyOS44MjQgNzguODc1QzEyOS45NzYgNzguMzkxNiAxMzAuMiA3Ny45NjY4IDEzMC40OTggNzcuNjAwNkMxMzAuNzk2IDc3LjIzNDQgMTMxLjE2OSA3Ni45NDg3IDEzMS42MTkgNzYuNzQzN0MxMzIuMDY4IDc2LjUzMzcgMTMyLjU5MyA3Ni40Mjg3IDEzMy4xOTMgNzYuNDI4N0MxMzMuODI4IDc2LjQyODcgMTM0LjM4NSA3Ni41NTU3IDEzNC44NjMgNzYuODA5NkMxMzUuMzQyIDc3LjA1ODYgMTM1LjcxOCA3Ny40MDc3IDEzNS45OTEgNzcuODU2OUMxMzYuMjcgNzguMzAxMyAxMzYuNDE0IDc4LjgxODggMTM2LjQyMyA3OS40MDk3SDEzNC43NjFDMTM0Ljc0NiA3OS4xMTY3IDEzNC42NzMgNzguODUzIDEzNC41NDEgNzguNjE4N0MxMzQuNDE0IDc4LjM3OTQgMTM0LjIzMyA3OC4xODkgMTMzLjk5OSA3OC4wNDc0QzEzMy43NyA3Ny45MDU4IDEzMy40OTQgNzcuODM1IDEzMy4xNzEgNzcuODM1QzEzMi44MTUgNzcuODM1IDEzMi41MiA3Ny45MDgyIDEzMi4yODUgNzguMDU0N0MxMzIuMDUxIDc4LjE5NjMgMTMxLjg2OCA3OC4zOTE2IDEzMS43MzYgNzguNjQwNkMxMzEuNjA0IDc4Ljg4NDggMTMxLjUwOSA3OS4xNjA2IDEzMS40NSA3OS40NjgzQzEzMS4zOTYgNzkuNzcxIDEzMS4zNyA4MC4wODU5IDEzMS4zNyA4MC40MTMxVjgwLjY2OTRDMTMxLjM3IDgwLjk5NjYgMTMxLjM5NiA4MS4zMTQgMTMxLjQ1IDgxLjYyMTZDMTMxLjUwNCA4MS45MjkyIDEzMS41OTcgODIuMjA1MSAxMzEuNzI5IDgyLjQ0OTJDMTMxLjg2NSA4Mi42ODg1IDEzMi4wNTEgODIuODgxMyAxMzIuMjg1IDgzLjAyNzhDMTMyLjUyIDgzLjE2OTQgMTMyLjgxNyA4My4yNDAyIDEzMy4xNzkgODMuMjQwMlpNMTM5LjUyMSA3My4yNVY4NC41SDEzNy43NjRWNzMuMjVIMTM5LjUyMVpNMTM5LjIxNCA4MC4yNDQ2TDEzOC42NDMgODAuMjM3M0MxMzguNjQ3IDc5LjY5MDQgMTM4LjcyMyA3OS4xODUxIDEzOC44NyA3OC43MjEyQzEzOS4wMjEgNzguMjU3MyAxMzkuMjMxIDc3Ljg1NDUgMTM5LjUgNzcuNTEyN0MxMzkuNzczIDc3LjE2NiAxNDAuMSA3Ni44OTk5IDE0MC40ODEgNzYuNzE0NEMxNDAuODYyIDc2LjUyMzkgMTQxLjI4NCA3Ni40Mjg3IDE0MS43NDggNzYuNDI4N0MxNDIuMTM5IDc2LjQyODcgMTQyLjQ5IDc2LjQ4MjQgMTQyLjgwMyA3Ni41ODk4QzE0My4xMiA3Ni42OTczIDE0My4zOTQgNzYuODcwNiAxNDMuNjIzIDc3LjEwOTlDMTQzLjg1MyA3Ny4zNDQyIDE0NC4wMjYgNzcuNjUxOSAxNDQuMTQzIDc4LjAzMjdDMTQ0LjI2NSA3OC40MDg3IDE0NC4zMjYgNzguODY3NyAxNDQuMzI2IDc5LjQwOTdWODQuNUgxNDIuNTU0Vjc5LjM5NUMxNDIuNTU0IDc5LjAxNDIgMTQyLjQ5OCA3OC43MTE0IDE0Mi4zODUgNzguNDg2OEMxNDIuMjc4IDc4LjI2MjIgMTQyLjExOSA3OC4xMDExIDE0MS45MDkgNzguMDAzNEMxNDEuNjk5IDc3LjkwMDkgMTQxLjQ0MyA3Ny44NDk2IDE0MS4xNCA3Ny44NDk2QzE0MC44MjMgNzcuODQ5NiAxNDAuNTQyIDc3LjkxMzEgMTQwLjI5OCA3OC4wNEMxNDAuMDU5IDc4LjE2NyAxMzkuODU4IDc4LjM0MDMgMTM5LjY5NyA3OC41NjAxQzEzOS41MzYgNzguNzc5OCAxMzkuNDE0IDc5LjAzMzcgMTM5LjMzMSA3OS4zMjE4QzEzOS4yNTMgNzkuNjA5OSAxMzkuMjE0IDc5LjkxNzUgMTM5LjIxNCA4MC4yNDQ2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzM2MzlfMTU1Njc0Ij4KPHJlY3QgeD0iMC41IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", - "description": "Sends the command to the device or updates attribute/time-series when the user toggles the slider. Widget settings will enable you to configure behavior how to fetch the initial state and what to trigger when turn on/off states.", + "image": "tb-image:c2luZ2xlLXN3aXRjaC5zdmc=:IlNpbmdsZSBTd2l0Y2giIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMzEuNTc3MSIgeT0iNjUuMDc0MiIgd2lkdGg9IjU2Ljk5MDEiIGhlaWdodD0iMjkuODUyIiByeD0iMTQuOTI2IiBmaWxsPSIjNTQ2OUZGIi8+CjxjaXJjbGUgY3g9IjczLjY0MDkiIGN5PSI4MC4wMDAzIiByPSIxMi4yMTIyIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTA5LjI0MSA4My40NzE3QzEwOS4yNDEgODMuMTQ5NCAxMDkuMTkxIDgyLjg2MyAxMDkuMDkxIDgyLjYxMjNDMTA4Ljk5OCA4Mi4zNjE3IDEwOC44MjkgODIuMTMyNSAxMDguNTg2IDgxLjkyNDhDMTA4LjM0MiA4MS43MTcxIDEwNy45OTkgODEuNTE2NiAxMDcuNTU1IDgxLjMyMzJDMTA3LjExOCA4MS4xMjI3IDEwNi41NTkgODAuOTE4NiAxMDUuODc5IDgwLjcxMDlDMTA1LjEzNCA4MC40ODE4IDEwNC40NDcgODAuMjI3NSAxMDMuODE2IDc5Ljk0ODJDMTAzLjE5MyA3OS42NjE4IDEwMi42NDkgNzkuMzMyNCAxMDIuMTg0IDc4Ljk2QzEwMS43MTggNzguNTgwNCAxMDEuMzU2IDc4LjE0NzEgMTAxLjA5OSA3Ny42NjAyQzEwMC44NDEgNzcuMTY2IDEwMC43MTIgNzYuNTk2NyAxMDAuNzEyIDc1Ljk1MjFDMTAwLjcxMiA3NS4zMTQ4IDEwMC44NDQgNzQuNzM0NyAxMDEuMTA5IDc0LjIxMTlDMTAxLjM4MiA3My42ODkxIDEwMS43NjUgNzMuMjM4IDEwMi4yNTkgNzIuODU4NEMxMDIuNzYgNzIuNDcxNyAxMDMuMzUxIDcyLjE3NDUgMTA0LjAzMSA3MS45NjY4QzEwNC43MTIgNzEuNzUyIDEwNS40NjQgNzEuNjQ0NSAxMDYuMjg3IDcxLjY0NDVDMTA3LjQ0NyA3MS42NDQ1IDEwOC40NDYgNzEuODU5NCAxMDkuMjg0IDcyLjI4OTFDMTEwLjEyOSA3Mi43MTg4IDExMC43NzcgNzMuMjk1MiAxMTEuMjI5IDc0LjAxODZDMTExLjY4NyA3NC43NDE5IDExMS45MTYgNzUuNTQwNCAxMTEuOTE2IDc2LjQxNDFIMTA5LjI0MUMxMDkuMjQxIDc1Ljg5ODQgMTA5LjEzIDc1LjQ0MzcgMTA4LjkwOCA3NS4wNDk4QzEwOC42OTMgNzQuNjQ4OCAxMDguMzY0IDc0LjMzMzcgMTA3LjkyIDc0LjEwNDVDMTA3LjQ4MyA3My44NzUzIDEwNi45MjggNzMuNzYwNyAxMDYuMjU1IDczLjc2MDdDMTA1LjYxOCA3My43NjA3IDEwNS4wODggNzMuODU3NCAxMDQuNjY1IDc0LjA1MDhDMTA0LjI0MyA3NC4yNDQxIDEwMy45MjcgNzQuNTA1NSAxMDMuNzIgNzQuODM1QzEwMy41MTIgNzUuMTY0NCAxMDMuNDA4IDc1LjUzNjggMTAzLjQwOCA3NS45NTIxQzEwMy40MDggNzYuMjQ1OCAxMDMuNDc2IDc2LjUxNDMgMTAzLjYxMiA3Ni43NTc4QzEwMy43NDggNzYuOTk0MSAxMDMuOTU2IDc3LjIxNjEgMTA0LjIzNSA3Ny40MjM4QzEwNC41MTUgNzcuNjI0MyAxMDQuODY2IDc3LjgxNDEgMTA1LjI4OCA3Ny45OTMyQzEwNS43MTEgNzguMTcyMiAxMDYuMjA4IDc4LjM0NDEgMTA2Ljc4MSA3OC41MDg4QzEwNy42NDggNzguNzY2NiAxMDguNDAzIDc5LjA1MzEgMTA5LjA0OCA3OS4zNjgyQzEwOS42OTIgNzkuNjc2MSAxMTAuMjI5IDgwLjAyNyAxMTAuNjU5IDgwLjQyMDlDMTExLjA4OSA4MC44MTQ4IDExMS40MTEgODEuMjYyNCAxMTEuNjI2IDgxLjc2MzdDMTExLjg0MSA4Mi4yNTc4IDExMS45NDggODIuODIgMTExLjk0OCA4My40NTAyQzExMS45NDggODQuMTA5IDExMS44MTYgODQuNzAzNSAxMTEuNTUxIDg1LjIzMzRDMTExLjI4NiA4NS43NTYyIDExMC45MDYgODYuMjAzOCAxMTAuNDEyIDg2LjU3NjJDMTA5LjkyNSA4Ni45NDE0IDEwOS4zMzggODcuMjI0MyAxMDguNjUgODcuNDI0OEMxMDcuOTcgODcuNjE4MiAxMDcuMjExIDg3LjcxNDggMTA2LjM3MyA4Ny43MTQ4QzEwNS42MjEgODcuNzE0OCAxMDQuODggODcuNjE0NiAxMDQuMTQ5IDg3LjQxNDFDMTAzLjQyNiA4Ny4yMTM1IDEwMi43NjcgODYuOTA5MiAxMDIuMTczIDg2LjUwMUMxMDEuNTc4IDg2LjA4NTYgMTAxLjEwNiA4NS41NyAxMDAuNzU1IDg0Ljk1NDFDMTAwLjQwNCA4NC4zMzExIDEwMC4yMjkgODMuNjA0MiAxMDAuMjI5IDgyLjc3MzRIMTAyLjkyNUMxMDIuOTI1IDgzLjI4MTkgMTAzLjAxMSA4My43MTUyIDEwMy4xODMgODQuMDczMkMxMDMuMzYyIDg0LjQzMTMgMTAzLjYwOSA4NC43MjQ5IDEwMy45MjQgODQuOTU0MUMxMDQuMjM5IDg1LjE3NjEgMTA0LjYwNCA4NS4zNDA4IDEwNS4wMiA4NS40NDgyQzEwNS40NDIgODUuNTU1NyAxMDUuODkzIDg1LjYwOTQgMTA2LjM3MyA4NS42MDk0QzEwNy4wMDMgODUuNjA5NCAxMDcuNTMgODUuNTE5OSAxMDcuOTUyIDg1LjM0MDhDMTA4LjM4MiA4NS4xNjE4IDEwOC43MDQgODQuOTExMSAxMDguOTE5IDg0LjU4ODlDMTA5LjEzNCA4NC4yNjY2IDEwOS4yNDEgODMuODk0MiAxMDkuMjQxIDgzLjQ3MTdaTTExNy41NzcgODQuOTIxOUwxMjAuMjYzIDc1Ljg3N0gxMjEuOTE3TDEyMS40NjYgNzguNTg0TDExOC43NTkgODcuNUgxMTcuMjc2TDExNy41NzcgODQuOTIxOVpNMTE1Ljk5OCA3NS44NzdMMTE4LjA5MyA4NC45NjQ4TDExOC4yNjUgODcuNUgxMTYuNjFMMTEzLjQ2MyA3NS44NzdIMTE1Ljk5OFpNMTI0LjQzMSA4NC44NTc0TDEyNi40NjEgNzUuODc3SDEyOC45ODVMMTI1Ljg0OSA4Ny41SDEyNC4xOTRMMTI0LjQzMSA4NC44NTc0Wk0xMjIuMTk2IDc1Ljg3N0wxMjQuODUgODQuODE0NUwxMjUuMTgzIDg3LjVIMTIzLjdMMTIwLjk2MSA3OC41NzMyTDEyMC41MSA3NS44NzdIMTIyLjE5NlpNMTMzLjg2MiA3NS44NzdWODcuNUgxMzEuMjYzVjc1Ljg3N0gxMzMuODYyWk0xMzEuMDkxIDcyLjgyNjJDMTMxLjA5MSA3Mi40MzIzIDEzMS4yMiA3Mi4xMDY0IDEzMS40NzggNzEuODQ4NkMxMzEuNzQzIDcxLjU4MzcgMTMyLjEwOCA3MS40NTEyIDEzMi41NzMgNzEuNDUxMkMxMzMuMDMyIDcxLjQ1MTIgMTMzLjM5MyA3MS41ODM3IDEzMy42NTggNzEuODQ4NkMxMzMuOTIzIDcyLjEwNjQgMTM0LjA1NiA3Mi40MzIzIDEzNC4wNTYgNzIuODI2MkMxMzQuMDU2IDczLjIxMjkgMTMzLjkyMyA3My41MzUyIDEzMy42NTggNzMuNzkzQzEzMy4zOTMgNzQuMDUwOCAxMzMuMDMyIDc0LjE3OTcgMTMyLjU3MyA3NC4xNzk3QzEzMi4xMDggNzQuMTc5NyAxMzEuNzQzIDc0LjA1MDggMTMxLjQ3OCA3My43OTNDMTMxLjIyIDczLjUzNTIgMTMxLjA5MSA3My4yMTI5IDEzMS4wOTEgNzIuODI2MlpNMTQyLjM3IDc1Ljg3N1Y3Ny43Njc2SDEzNS44MTdWNzUuODc3SDE0Mi4zN1pNMTM3LjcwOCA3My4wMzAzSDE0MC4yOTdWODQuMjg4MUMxNDAuMjk3IDg0LjY0NjIgMTQwLjM0NyA4NC45MjE5IDE0MC40NDcgODUuMTE1MkMxNDAuNTU1IDg1LjMwMTQgMTQwLjcwMSA4NS40MjY4IDE0MC44ODggODUuNDkxMkMxNDEuMDc0IDg1LjU1NTcgMTQxLjI5MiA4NS41ODc5IDE0MS41NDMgODUuNTg3OUMxNDEuNzIyIDg1LjU4NzkgMTQxLjg5NCA4NS41NzcxIDE0Mi4wNTkgODUuNTU1N0MxNDIuMjIzIDg1LjUzNDIgMTQyLjM1NiA4NS41MTI3IDE0Mi40NTYgODUuNDkxMkwxNDIuNDY3IDg3LjQ2NzhDMTQyLjI1MiA4Ny41MzIyIDE0Mi4wMDEgODcuNTg5NSAxNDEuNzE1IDg3LjYzOTZDMTQxLjQzNiA4Ny42ODk4IDE0MS4xMTMgODcuNzE0OCAxNDAuNzQ4IDg3LjcxNDhDMTQwLjE1NCA4Ny43MTQ4IDEzOS42MjcgODcuNjExIDEzOS4xNjkgODcuNDAzM0MxMzguNzExIDg3LjE4ODUgMTM4LjM1MyA4Ni44NDExIDEzOC4wOTUgODYuMzYxM0MxMzcuODM3IDg1Ljg4MTUgMTM3LjcwOCA4NS4yNDQxIDEzNy43MDggODQuNDQ5MlY3My4wMzAzWk0xNDkuNDYgODUuNjUyM0MxNDkuODgyIDg1LjY1MjMgMTUwLjI2MiA4NS41NyAxNTAuNTk5IDg1LjQwNTNDMTUwLjk0MiA4NS4yMzM0IDE1MS4yMTggODQuOTk3MSAxNTEuNDI2IDg0LjY5NjNDMTUxLjY0MSA4NC4zOTU1IDE1MS43NTkgODQuMDQ4MiAxNTEuNzggODMuNjU0M0gxNTQuMjE5QzE1NC4yMDQgODQuNDA2MiAxNTMuOTgyIDg1LjA5MDIgMTUzLjU1MyA4NS43MDYxQzE1My4xMjMgODYuMzIxOSAxNTIuNTU0IDg2LjgxMjUgMTUxLjg0NSA4Ny4xNzc3QzE1MS4xMzYgODcuNTM1OCAxNTAuMzUyIDg3LjcxNDggMTQ5LjQ5MiA4Ny43MTQ4QzE0OC42MDQgODcuNzE0OCAxNDcuODMxIDg3LjU2NDUgMTQ3LjE3MiA4Ny4yNjM3QzE0Ni41MTMgODYuOTU1NyAxNDUuOTY1IDg2LjUzMzIgMTQ1LjUyOCA4NS45OTYxQzE0NS4wOTEgODUuNDU5IDE0NC43NjIgODQuODM5NSAxNDQuNTQgODQuMTM3N0MxNDQuMzI1IDgzLjQzNTkgMTQ0LjIxOCA4Mi42ODM5IDE0NC4yMTggODEuODgxOFY4MS41MDU5QzE0NC4yMTggODAuNzAzOCAxNDQuMzI1IDc5Ljk1MTggMTQ0LjU0IDc5LjI1QzE0NC43NjIgNzguNTQxIDE0NS4wOTEgNzcuOTE4IDE0NS41MjggNzcuMzgwOUMxNDUuOTY1IDc2Ljg0MzggMTQ2LjUxMyA3Ni40MjQ4IDE0Ny4xNzIgNzYuMTI0QzE0Ny44MzEgNzUuODE2MSAxNDguNjAxIDc1LjY2MjEgMTQ5LjQ4MSA3NS42NjIxQzE1MC40MTIgNzUuNjYyMSAxNTEuMjI5IDc1Ljg0ODMgMTUxLjkzMSA3Ni4yMjA3QzE1Mi42MzIgNzYuNTg1OSAxNTMuMTg0IDc3LjA5OCAxNTMuNTg1IDc3Ljc1NjhDMTUzLjk5MyA3OC40MDg1IDE1NC4yMDQgNzkuMTY3NiAxNTQuMjE5IDgwLjAzNDJIMTUxLjc4QzE1MS43NTkgNzkuNjA0NSAxNTEuNjUxIDc5LjIxNzggMTUxLjQ1OCA3OC44NzRDMTUxLjI3MiA3OC41MjMxIDE1MS4wMDcgNzguMjQzOCAxNTAuNjYzIDc4LjAzNjFDMTUwLjMyNiA3Ny44Mjg1IDE0OS45MjIgNzcuNzI0NiAxNDkuNDQ5IDc3LjcyNDZDMTQ4LjkyNiA3Ny43MjQ2IDE0OC40OTMgNzcuODMyIDE0OC4xNDkgNzguMDQ2OUMxNDcuODA2IDc4LjI1NDYgMTQ3LjUzNyA3OC41NDEgMTQ3LjM0NCA3OC45MDYyQzE0Ny4xNSA3OS4yNjQzIDE0Ny4wMTEgNzkuNjY4OSAxNDYuOTI1IDgwLjEyMDFDMTQ2Ljg0NiA4MC41NjQxIDE0Ni44MDcgODEuMDI2IDE0Ni44MDcgODEuNTA1OVY4MS44ODE4QzE0Ni44MDcgODIuMzYxNyAxNDYuODQ2IDgyLjgyNzEgMTQ2LjkyNSA4My4yNzgzQzE0Ny4wMDQgODMuNzI5NSAxNDcuMTQgODQuMTM0MSAxNDcuMzMzIDg0LjQ5MjJDMTQ3LjUzNCA4NC44NDMxIDE0Ny44MDYgODUuMTI2IDE0OC4xNDkgODUuMzQwOEMxNDguNDkzIDg1LjU0ODUgMTQ4LjkzIDg1LjY1MjMgMTQ5LjQ2IDg1LjY1MjNaTTE1OS4xMDYgNzFWODcuNUgxNTYuNTI4VjcxSDE1OS4xMDZaTTE1OC42NTUgODEuMjU4OEwxNTcuODE3IDgxLjI0OEMxNTcuODI1IDgwLjQ0NiAxNTcuOTM2IDc5LjcwNDggMTU4LjE1IDc5LjAyNDRDMTU4LjM3MiA3OC4zNDQxIDE1OC42OCA3Ny43NTMzIDE1OS4wNzQgNzcuMjUyQzE1OS40NzUgNzYuNzQzNSAxNTkuOTU1IDc2LjM1MzIgMTYwLjUxNCA3Ni4wODExQzE2MS4wNzIgNzUuODAxOCAxNjEuNjkyIDc1LjY2MjEgMTYyLjM3MiA3NS42NjIxQzE2Mi45NDUgNzUuNjYyMSAxNjMuNDYxIDc1Ljc0MDkgMTYzLjkxOSA3NS44OTg0QzE2NC4zODQgNzYuMDU2IDE2NC43ODUgNzYuMzEwMiAxNjUuMTIyIDc2LjY2MTFDMTY1LjQ1OSA3Ny4wMDQ5IDE2NS43MTMgNzcuNDU2MSAxNjUuODg1IDc4LjAxNDZDMTY2LjA2NCA3OC41NjYxIDE2Ni4xNTMgNzkuMjM5MyAxNjYuMTUzIDgwLjAzNDJWODcuNUgxNjMuNTU0VjgwLjAxMjdDMTYzLjU1NCA3OS40NTQxIDE2My40NzEgNzkuMDEwMSAxNjMuMzA3IDc4LjY4MDdDMTYzLjE0OSA3OC4zNTEyIDE2Mi45MTYgNzguMTE0OSAxNjIuNjA4IDc3Ljk3MTdDMTYyLjMgNzcuODIxMyAxNjEuOTI0IDc3Ljc0NjEgMTYxLjQ4IDc3Ljc0NjFDMTYxLjAxNSA3Ny43NDYxIDE2MC42MDMgNzcuODM5MiAxNjAuMjQ1IDc4LjAyNTRDMTU5Ljg5NCA3OC4yMTE2IDE1OS42MDEgNzguNDY1OCAxNTkuMzY0IDc4Ljc4ODFDMTU5LjEyOCA3OS4xMTA0IDE1OC45NDkgNzkuNDgyNyAxNTguODI3IDc5LjkwNTNDMTU4LjcxMyA4MC4zMjc4IDE1OC42NTUgODAuNzc5IDE1OC42NTUgODEuMjU4OFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9zdmc+Cg==", + "description": "Allows users to toggle a slider to send commands to devices or update attributes/time-series data. Configurable settings let users define how to retrieve the initial state and specify actions for the on/off toggle.", "descriptor": { "type": "rpc", "sizeX": 3.5, @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rpcWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '280px',\n previewHeight: '80px',\n embedTitlePanel: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '280px',\n previewHeight: '80px',\n embedTitlePanel: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-single-switch-widget-settings", @@ -30,6 +30,9 @@ "interface", "subroutine call", "inter-process communication", - "server request" + "server request", + "update attribute", + "set attribute", + "add time-series" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/slider.json b/application/src/main/data/json/system/widget_types/slider.json new file mode 100644 index 0000000000..8cbf5326c3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/slider.json @@ -0,0 +1,39 @@ +{ + "fqn": "slider", + "name": "Slider", + "deprecated": false, + "image": "tb-image:SG9yaXpvbnRhbCBzbGlkZXIuc3Zn:IlNsaWRlciIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik05MC41MDY0IDYwLjkxOFY2My4xNjhINzguMjAxN0w3OC4xMDggNjEuNDY4OEw4NS40NjczIDQ5LjkzNzVIODcuNzI5MUw4NS4yNzk4IDU0LjEzMjhMODEuMDQ5NCA2MC45MThIOTAuNTA2NFpNODguMzczNiA0OS45Mzc1VjY3SDg1LjU0OTRWNDkuOTM3NUg4OC4zNzM2Wk0xMDMuNjA5IDYyLjM0NzdDMTAzLjYwOSA2My40MTAyIDEwMy4zNjMgNjQuMzA0NyAxMDIuODcxIDY1LjAzMTJDMTAyLjM3OSA2NS43NTc4IDEwMS43MDcgNjYuMzA4NiAxMDAuODU1IDY2LjY4MzZDMTAwLjAxMiA2Ny4wNTA4IDk5LjA1ODYgNjcuMjM0NCA5Ny45OTYxIDY3LjIzNDRDOTYuOTMzNiA2Ny4yMzQ0IDk1Ljk3NjYgNjcuMDUwOCA5NS4xMjUgNjYuNjgzNkM5NC4yNzM0IDY2LjMwODYgOTMuNjAxNiA2NS43NTc4IDkzLjEwOTQgNjUuMDMxMkM5Mi42MTcyIDY0LjMwNDcgOTIuMzcxMSA2My40MTAyIDkyLjM3MTEgNjIuMzQ3N0M5Mi4zNzExIDYxLjY0NDUgOTIuNTA3OCA2MS4wMDc4IDkyLjc4MTMgNjAuNDM3NUM5My4wNTQ3IDU5Ljg1OTQgOTMuNDQxNCA1OS4zNjMzIDkzLjk0MTQgNTguOTQ5MkM5NC40NDkyIDU4LjUyNzMgOTUuMDQzIDU4LjIwMzEgOTUuNzIyNyA1Ny45NzY2Qzk2LjQxMDIgNTcuNzUgOTcuMTYwMiA1Ny42MzY3IDk3Ljk3MjcgNTcuNjM2N0M5OS4wNTA4IDU3LjYzNjcgMTAwLjAxNiA1Ny44MzU5IDEwMC44NjcgNTguMjM0NEMxMDEuNzE5IDU4LjYzMjggMTAyLjM4NyA1OS4xODM2IDEwMi44NzEgNTkuODg2N0MxMDMuMzYzIDYwLjU4OTggMTAzLjYwOSA2MS40MTAyIDEwMy42MDkgNjIuMzQ3N1pNMTAwLjc3MyA2Mi4yMDdDMTAwLjc3MyA2MS42MzY3IDEwMC42NTYgNjEuMTM2NyAxMDAuNDIyIDYwLjcwN0MxMDAuMTg4IDYwLjI3NzMgOTkuODU5NCA1OS45NDUzIDk5LjQzNzUgNTkuNzEwOUM5OS4wMTU2IDU5LjQ3NjYgOTguNTI3MyA1OS4zNTk0IDk3Ljk3MjcgNTkuMzU5NEM5Ny40MTAyIDU5LjM1OTQgOTYuOTIxOSA1OS40NzY2IDk2LjUwNzggNTkuNzEwOUM5Ni4wOTM4IDU5Ljk0NTMgOTUuNzY5NSA2MC4yNzczIDk1LjUzNTIgNjAuNzA3Qzk1LjMwODYgNjEuMTM2NyA5NS4xOTUzIDYxLjYzNjcgOTUuMTk1MyA2Mi4yMDdDOTUuMTk1MyA2Mi43ODUyIDk1LjMwODYgNjMuMjg1MiA5NS41MzUyIDYzLjcwN0M5NS43NjE3IDY0LjEyMTEgOTYuMDg1OSA2NC40Mzc1IDk2LjUwNzggNjQuNjU2MkM5Ni45Mjk3IDY0Ljg3NSA5Ny40MjU4IDY0Ljk4NDQgOTcuOTk2MSA2NC45ODQ0Qzk4LjU2NjQgNjQuOTg0NCA5OS4wNTg2IDY0Ljg3NSA5OS40NzI3IDY0LjY1NjJDOTkuODg2NyA2NC40Mzc1IDEwMC4yMDcgNjQuMTIxMSAxMDAuNDM0IDYzLjcwN0MxMDAuNjYgNjMuMjg1MiAxMDAuNzczIDYyLjc4NTIgMTAwLjc3MyA2Mi4yMDdaTTEwMy4yMjMgNTQuNDI1OEMxMDMuMjIzIDU1LjI3NzMgMTAyLjk5NiA1Ni4wMzUyIDEwMi41NDMgNTYuNjk5MkMxMDIuMDk4IDU3LjM2MzMgMTAxLjQ4IDU3Ljg4NjcgMTAwLjY5MSA1OC4yNjk1Qzk5LjkwMjMgNTguNjQ0NSA5OS4wMDM5IDU4LjgzMiA5Ny45OTYxIDU4LjgzMkM5Ni45ODA1IDU4LjgzMiA5Ni4wNzQyIDU4LjY0NDUgOTUuMjc3MyA1OC4yNjk1Qzk0LjQ4ODMgNTcuODg2NyA5My44NjcyIDU3LjM2MzMgOTMuNDE0MSA1Ni42OTkyQzkyLjk2ODggNTYuMDM1MiA5Mi43NDYxIDU1LjI3NzMgOTIuNzQ2MSA1NC40MjU4QzkyLjc0NjEgNTMuNDEwMiA5Mi45Njg4IDUyLjU1NDcgOTMuNDE0MSA1MS44NTk0QzkzLjg2NzIgNTEuMTU2MiA5NC40ODgzIDUwLjYyMTEgOTUuMjc3MyA1MC4yNTM5Qzk2LjA2NjQgNDkuODg2NyA5Ni45Njg4IDQ5LjcwMzEgOTcuOTg0NCA0OS43MDMxQzk5IDQ5LjcwMzEgOTkuOTAyMyA0OS44ODY3IDEwMC42OTEgNTAuMjUzOUMxMDEuNDggNTAuNjIxMSAxMDIuMDk4IDUxLjE1NjIgMTAyLjU0MyA1MS44NTk0QzEwMi45OTYgNTIuNTU0NyAxMDMuMjIzIDUzLjQxMDIgMTAzLjIyMyA1NC40MjU4Wk0xMDAuMzk4IDU0LjUxOTVDMTAwLjM5OCA1NC4wMTE3IDEwMC4yOTcgNTMuNTY2NCAxMDAuMDk0IDUzLjE4MzZDOTkuODk4NCA1Mi43OTMgOTkuNjIxMSA1Mi40ODgzIDk5LjI2MTcgNTIuMjY5NUM5OC45MDIzIDUyLjA1MDggOTguNDc2NiA1MS45NDE0IDk3Ljk4NDQgNTEuOTQxNEM5Ny40OTIyIDUxLjk0MTQgOTcuMDY2NCA1Mi4wNDY5IDk2LjcwNyA1Mi4yNTc4Qzk2LjM0NzcgNTIuNDY4OCA5Ni4wNzAzIDUyLjc2NTYgOTUuODc1IDUzLjE0ODRDOTUuNjc5NyA1My41MzEyIDk1LjU4MiA1My45ODgzIDk1LjU4MiA1NC41MTk1Qzk1LjU4MiA1NS4wNDMgOTUuNjc5NyA1NS41IDk1Ljg3NSA1NS44OTA2Qzk2LjA3MDMgNTYuMjczNCA5Ni4zNDc3IDU2LjU3NDIgOTYuNzA3IDU2Ljc5M0M5Ny4wNzQyIDU3LjAxMTcgOTcuNTAzOSA1Ny4xMjExIDk3Ljk5NjEgNTcuMTIxMUM5OC40ODgzIDU3LjEyMTEgOTguOTE0MSA1Ny4wMTE3IDk5LjI3MzQgNTYuNzkzQzk5LjYzMjggNTYuNTc0MiA5OS45MTAyIDU2LjI3MzQgMTAwLjEwNSA1NS44OTA2QzEwMC4zMDEgNTUuNSAxMDAuMzk4IDU1LjA0MyAxMDAuMzk4IDU0LjUxOTVaTTEwNi4wMzcgNTQuMTIxMVY1My4yMTg4QzEwNi4wMzcgNTIuNTcwMyAxMDYuMTc3IDUxLjk4MDUgMTA2LjQ1OCA1MS40NDkyQzEwNi43NCA1MC45MTggMTA3LjE1IDUwLjQ5MjIgMTA3LjY4OSA1MC4xNzE5QzEwOC4yMjggNDkuODUxNiAxMDguODc2IDQ5LjY5MTQgMTA5LjYzNCA0OS42OTE0QzExMC40MTUgNDkuNjkxNCAxMTEuMDcyIDQ5Ljg1MTYgMTExLjYwMyA1MC4xNzE5QzExMi4xNDIgNTAuNDkyMiAxMTIuNTUyIDUwLjkxOCAxMTIuODMzIDUxLjQ0OTJDMTEzLjExNSA1MS45ODA1IDExMy4yNTUgNTIuNTcwMyAxMTMuMjU1IDUzLjIxODhWNTQuMTIxMUMxMTMuMjU1IDU0Ljc1MzkgMTEzLjExNSA1NS4zMzU5IDExMi44MzMgNTUuODY3MkMxMTIuNTYgNTYuMzk4NCAxMTIuMTU0IDU2LjgyNDIgMTExLjYxNSA1Ny4xNDQ1QzExMS4wODMgNTcuNDY0OCAxMTAuNDMxIDU3LjYyNSAxMDkuNjU4IDU3LjYyNUMxMDguODkyIDU3LjYyNSAxMDguMjM2IDU3LjQ2NDggMTA3LjY4OSA1Ny4xNDQ1QzEwNy4xNSA1Ni44MjQyIDEwNi43NCA1Ni4zOTg0IDEwNi40NTggNTUuODY3MkMxMDYuMTc3IDU1LjMzNTkgMTA2LjAzNyA1NC43NTM5IDEwNi4wMzcgNTQuMTIxMVpNMTA3Ljk5NCA1My4yMTg4VjU0LjEyMTFDMTA3Ljk5NCA1NC40MzM2IDEwOC4wNTIgNTQuNzMwNSAxMDguMTY5IDU1LjAxMTdDMTA4LjI5NCA1NS4yOTMgMTA4LjQ4MiA1NS41MTk1IDEwOC43MzIgNTUuNjkxNEMxMDguOTgyIDU1Ljg2MzMgMTA5LjI5IDU1Ljk0OTIgMTA5LjY1OCA1NS45NDkyQzExMC4wMzMgNTUuOTQ5MiAxMTAuMzM3IDU1Ljg2MzMgMTEwLjU3MiA1NS42OTE0QzExMC44MTQgNTUuNTE5NSAxMTAuOTk0IDU1LjI5MyAxMTEuMTExIDU1LjAxMTdDMTExLjIyOCA1NC43MzA1IDExMS4yODcgNTQuNDMzNiAxMTEuMjg3IDU0LjEyMTFWNTMuMjE4OEMxMTEuMjg3IDUyLjg5ODQgMTExLjIyNCA1Mi41OTc3IDExMS4wOTkgNTIuMzE2NEMxMTAuOTgyIDUyLjAyNzMgMTEwLjgwMiA1MS43OTY5IDExMC41NiA1MS42MjVDMTEwLjMxOCA1MS40NTMxIDExMC4wMDkgNTEuMzY3MiAxMDkuNjM0IDUxLjM2NzJDMTA5LjI3NSA1MS4zNjcyIDEwOC45NyA1MS40NTMxIDEwOC43MiA1MS42MjVDMTA4LjQ3OCA1MS43OTY5IDEwOC4yOTQgNTIuMDI3MyAxMDguMTY5IDUyLjMxNjRDMTA4LjA1MiA1Mi41OTc3IDEwNy45OTQgNTIuODk4NCAxMDcuOTk0IDUzLjIxODhaTTExNC4yNjMgNjMuNzMwNVY2Mi44MTY0QzExNC4yNjMgNjIuMTc1OCAxMTQuNDA0IDYxLjU4OTggMTE0LjY4NSA2MS4wNTg2QzExNC45NzQgNjAuNTI3MyAxMTUuMzg4IDYwLjEwMTYgMTE1LjkyNyA1OS43ODEyQzExNi40NjYgNTkuNDYwOSAxMTcuMTE1IDU5LjMwMDggMTE3Ljg3MyA1OS4zMDA4QzExOC42NTQgNTkuMzAwOCAxMTkuMzEgNTkuNDYwOSAxMTkuODQxIDU5Ljc4MTJDMTIwLjM4IDYwLjEwMTYgMTIwLjc4NyA2MC41MjczIDEyMS4wNiA2MS4wNTg2QzEyMS4zNDEgNjEuNTg5OCAxMjEuNDgyIDYyLjE3NTggMTIxLjQ4MiA2Mi44MTY0VjYzLjczMDVDMTIxLjQ4MiA2NC4zNzExIDEyMS4zNDEgNjQuOTU3IDEyMS4wNiA2NS40ODgzQzEyMC43ODcgNjYuMDE5NSAxMjAuMzg0IDY2LjQ0NTMgMTE5Ljg1MyA2Ni43NjU2QzExOS4zMjIgNjcuMDg1OSAxMTguNjczIDY3LjI0NjEgMTE3LjkwOCA2Ny4yNDYxQzExNy4xMzQgNjcuMjQ2MSAxMTYuNDc0IDY3LjA4NTkgMTE1LjkyNyA2Ni43NjU2QzExNS4zODggNjYuNDQ1MyAxMTQuOTc0IDY2LjAxOTUgMTE0LjY4NSA2NS40ODgzQzExNC40MDQgNjQuOTU3IDExNC4yNjMgNjQuMzcxMSAxMTQuMjYzIDYzLjczMDVaTTExNi4yMzIgNjIuODE2NFY2My43MzA1QzExNi4yMzIgNjQuMDQzIDExNi4yOTQgNjQuMzM5OCAxMTYuNDE5IDY0LjYyMTFDMTE2LjU1MiA2NC45MDIzIDExNi43NDQgNjUuMTMyOCAxMTYuOTk0IDY1LjMxMjVDMTE3LjI0NCA2NS40ODQ0IDExNy41NDQgNjUuNTcwMyAxMTcuODk2IDY1LjU3MDNDMTE4LjI5NCA2NS41NzAzIDExOC42MTUgNjUuNDg0NCAxMTguODU3IDY1LjMxMjVDMTE5LjA5OSA2NS4xMzI4IDExOS4yNzEgNjQuOTA2MiAxMTkuMzczIDY0LjYzMjhDMTE5LjQ4MiA2NC4zNTE2IDExOS41MzcgNjQuMDUwOCAxMTkuNTM3IDYzLjczMDVWNjIuODE2NEMxMTkuNTM3IDYyLjQ5NjEgMTE5LjQ3NCA2Mi4xOTUzIDExOS4zNDkgNjEuOTE0MUMxMTkuMjMyIDYxLjYzMjggMTE5LjA0OCA2MS40MDYyIDExOC43OTggNjEuMjM0NEMxMTguNTU2IDYxLjA2MjUgMTE4LjI0OCA2MC45NzY2IDExNy44NzMgNjAuOTc2NkMxMTcuNTA1IDYwLjk3NjYgMTE3LjIwMSA2MS4wNjI1IDExNi45NTggNjEuMjM0NEMxMTYuNzE2IDYxLjQwNjIgMTE2LjUzMyA2MS42MzI4IDExNi40MDggNjEuOTE0MUMxMTYuMjkgNjIuMTk1MyAxMTYuMjMyIDYyLjQ5NjEgMTE2LjIzMiA2Mi44MTY0Wk0xMTguNTc2IDUyLjM3NUwxMTAuMjQ0IDY1LjcxMDlMMTA4LjgwMiA2NC44Nzg5TDExNy4xMzQgNTEuNTQzTDExOC41NzYgNTIuMzc1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cmVjdCB5PSI3OSIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI4IiByeD0iMi43MDE3NCIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjMiLz4KPGNpcmNsZSBjeD0iMi45MTk0NiIgY3k9IjgzIiByPSIwLjU2ODg3OCIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjE4LjI1MjUiIGN5PSI4MyIgcj0iMC41Njg4NzgiIGZpbGw9IiM1NDY5RkYiIGZpbGwtb3BhY2l0eT0iMC44MiIvPgo8Y2lyY2xlIGN4PSIzMy41ODQ1IiBjeT0iODMiIHI9IjAuNTY4ODc4IiBmaWxsPSIjNTQ2OUZGIiBmaWxsLW9wYWNpdHk9IjAuODIiLz4KPGNpcmNsZSBjeD0iNDguOTE2NSIgY3k9IjgzIiByPSIwLjU2ODg3OCIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjY0LjI0ODYiIGN5PSI4MyIgcj0iMC41Njg4NzgiIGZpbGw9IiM1NDY5RkYiIGZpbGwtb3BhY2l0eT0iMC44MiIvPgo8Y2lyY2xlIGN4PSI3OS41ODA2IiBjeT0iODMiIHI9IjAuNTY4ODc4IiBmaWxsPSIjNTQ2OUZGIiBmaWxsLW9wYWNpdHk9IjAuODIiLz4KPGNpcmNsZSBjeD0iOTQuOTEzNiIgY3k9IjgzIiByPSIwLjU2ODg3OCIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjExMC42NzciIGN5PSI4MyIgcj0iMSIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjEyNi44NzEiIGN5PSI4MyIgcj0iMSIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjE0My4wNjUiIGN5PSI4MyIgcj0iMSIgZmlsbD0iIzU0NjlGRiIgZmlsbC1vcGFjaXR5PSIwLjgyIi8+CjxjaXJjbGUgY3g9IjE1OS4yNiIgY3k9IjgzIiByPSIxIiBmaWxsPSIjNTQ2OUZGIiBmaWxsLW9wYWNpdHk9IjAuODIiLz4KPGNpcmNsZSBjeD0iMTc1LjQ1NSIgY3k9IjgzIiByPSIxIiBmaWxsPSIjNTQ2OUZGIiBmaWxsLW9wYWNpdHk9IjAuODIiLz4KPGNpcmNsZSBjeD0iMTkxLjY0OSIgY3k9IjgzIiByPSIxIiBmaWxsPSIjNTQ2OUZGIiBmaWxsLW9wYWNpdHk9IjAuODIiLz4KPHJlY3QgeT0iNzkiIHdpZHRoPSIxMDEiIGhlaWdodD0iOCIgcng9IjIuNzAxNzQiIGZpbGw9IiM1NDY5RkYiLz4KPHBhdGggZD0iTTUuNTU5MDggOTUuNDY2OFY5Ni42NTkyQzUuNTU5MDggOTcuMzAwMSA1LjUwMTc5IDk3Ljg0MDggNS4zODcyMSA5OC4yODEyQzUuMjcyNjIgOTguNzIxNyA1LjEwNzkxIDk5LjA3NjIgNC44OTMwNyA5OS4zNDQ3QzQuNjc4MjIgOTkuNjEzMyA0LjQxODYyIDk5LjgwODQgNC4xMTQyNiA5OS45MzAyQzMuODEzNDggMTAwLjA0OCAzLjQ3MzMxIDEwMC4xMDcgMy4wOTM3NSAxMDAuMTA3QzIuNzkyOTcgMTAwLjEwNyAyLjUxNTQ2IDEwMC4wNyAyLjI2MTIzIDk5Ljk5NDZDMi4wMDcgOTkuOTE5NCAxLjc3NzgzIDk5Ljc5OTUgMS41NzM3MyA5OS42MzQ4QzEuMzczMjEgOTkuNDY2NSAxLjIwMTMzIDk5LjI0OCAxLjA1ODExIDk4Ljk3OTVDMC45MTQ4NzYgOTguNzEwOSAwLjgwNTY2NCA5OC4zODUxIDAuNzMwNDY5IDk4LjAwMkMwLjY1NTI3MyA5Ny42MTg4IDAuNjE3Njc2IDk3LjE3MTIgMC42MTc2NzYgOTYuNjU5MlY5NS40NjY4QzAuNjE3Njc2IDk0LjgyNTggMC42NzQ5NjcgOTQuMjg4NyAwLjc4OTU1MSA5My44NTU1QzAuOTA3NzE1IDkzLjQyMjIgMS4wNzQyMiA5My4wNzQ5IDEuMjg5MDYgOTIuODEzNUMxLjUwMzkxIDkyLjU0ODUgMS43NjE3MiA5Mi4zNTg3IDIuMDYyNSA5Mi4yNDQxQzIuMzY2ODYgOTIuMTI5NiAyLjcwNzAzIDkyLjA3MjMgMy4wODMwMSA5Mi4wNzIzQzMuMzg3MzcgOTIuMDcyMyAzLjY2NjY3IDkyLjEwOTkgMy45MjA5IDkyLjE4NTFDNC4xNzg3MSA5Mi4yNTY3IDQuNDA3ODggOTIuMzczIDQuNjA4NCA5Mi41MzQyQzQuODA4OTIgOTIuNjkxNyA0Ljk3OSA5Mi45MDMgNS4xMTg2NSA5My4xNjhDNS4yNjE4OCA5My40Mjk0IDUuMzcxMDkgOTMuNzQ5OCA1LjQ0NjI5IDk0LjEyOTRDNS41MjE0OCA5NC41MDkgNS41NTkwOCA5NC45NTQ4IDUuNTU5MDggOTUuNDY2OFpNNC41NjAwNiA5Ni44MjAzVjk1LjMwMDNDNC41NjAwNiA5NC45NDk0IDQuNTM4NTcgOTQuNjQxNCA0LjQ5NTYxIDk0LjM3NjVDNC40NTYyMiA5NC4xMDc5IDQuMzk3MTQgOTMuODc4NyA0LjMxODM2IDkzLjY4OUM0LjIzOTU4IDkzLjQ5OTIgNC4xMzkzMiA5My4zNDUyIDQuMDE3NTggOTMuMjI3MUMzLjg5OTQxIDkzLjEwODkgMy43NjE1NiA5My4wMjI5IDMuNjA0IDkyLjk2OTJDMy40NTAwMyA5Mi45MTE5IDMuMjc2MzcgOTIuODgzMyAzLjA4MzAxIDkyLjg4MzNDMi44NDY2OCA5Mi44ODMzIDIuNjM3MjEgOTIuOTI4MSAyLjQ1NDU5IDkzLjAxNzZDMi4yNzE5NyA5My4xMDM1IDIuMTE4IDkzLjI0MTQgMS45OTI2OCA5My40MzEyQzEuODcwOTMgOTMuNjIwOSAxLjc3NzgzIDkzLjg2OTggMS43MTMzOCA5NC4xNzc3QzEuNjQ4OTMgOTQuNDg1NyAxLjYxNjcgOTQuODU5OSAxLjYxNjcgOTUuMzAwM1Y5Ni44MjAzQzEuNjE2NyA5Ny4xNzEyIDEuNjM2MzkgOTcuNDgxIDEuNjc1NzggOTcuNzQ5NUMxLjcxODc1IDk4LjAxODEgMS43ODE0MSA5OC4yNTA4IDEuODYzNzcgOTguNDQ3OEMxLjk0NjEzIDk4LjY0MTEgMi4wNDYzOSA5OC44MDA1IDIuMTY0NTUgOTguOTI1OEMyLjI4MjcxIDk5LjA1MTEgMi40MTg3OCA5OS4xNDQyIDIuNTcyNzUgOTkuMjA1MUMyLjczMDMxIDk5LjI2MjQgMi45MDM5NyA5OS4yOTEgMy4wOTM3NSA5OS4yOTFDMy4zMzcyNCA5OS4yOTEgMy41NTAyOSA5OS4yNDQ1IDMuNzMyOTEgOTkuMTUxNEMzLjkxNTUzIDk5LjA1ODMgNC4wNjc3MSA5OC45MTMyIDQuMTg5NDUgOTguNzE2M0M0LjMxNDc4IDk4LjUxNTggNC40MDc4OCA5OC4yNTk4IDQuNDY4NzUgOTcuOTQ4MkM0LjUyOTYyIDk3LjYzMzEgNC41NjAwNiA5Ny4yNTcyIDQuNTYwMDYgOTYuODIwM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTE4NC44NTMgOTIuMTM2N1YxMDBIMTgzLjg1OVY5My4zNzc0TDE4MS44NTYgOTQuMTA3OVY5My4yMTA5TDE4NC42OTcgOTIuMTM2N0gxODQuODUzWk0xOTIuOTM0IDk1LjQ2NjhWOTYuNjU5MkMxOTIuOTM0IDk3LjMwMDEgMTkyLjg3NyA5Ny44NDA4IDE5Mi43NjIgOTguMjgxMkMxOTIuNjQ4IDk4LjcyMTcgMTkyLjQ4MyA5OS4wNzYyIDE5Mi4yNjggOTkuMzQ0N0MxOTIuMDUzIDk5LjYxMzMgMTkxLjc5NCA5OS44MDg0IDE5MS40ODkgOTkuOTMwMkMxOTEuMTg4IDEwMC4wNDggMTkwLjg0OCAxMDAuMTA3IDE5MC40NjkgMTAwLjEwN0MxOTAuMTY4IDEwMC4xMDcgMTg5Ljg5IDEwMC4wNyAxODkuNjM2IDk5Ljk5NDZDMTg5LjM4MiA5OS45MTk0IDE4OS4xNTMgOTkuNzk5NSAxODguOTQ5IDk5LjYzNDhDMTg4Ljc0OCA5OS40NjY1IDE4OC41NzYgOTkuMjQ4IDE4OC40MzMgOTguOTc5NUMxODguMjkgOTguNzEwOSAxODguMTgxIDk4LjM4NTEgMTg4LjEwNSA5OC4wMDJDMTg4LjAzIDk3LjYxODggMTg3Ljk5MyA5Ny4xNzEyIDE4Ny45OTMgOTYuNjU5MlY5NS40NjY4QzE4Ny45OTMgOTQuODI1OCAxODguMDUgOTQuMjg4NyAxODguMTY1IDkzLjg1NTVDMTg4LjI4MyA5My40MjIyIDE4OC40NDkgOTMuMDc0OSAxODguNjY0IDkyLjgxMzVDMTg4Ljg3OSA5Mi41NDg1IDE4OS4xMzcgOTIuMzU4NyAxODkuNDM4IDkyLjI0NDFDMTg5Ljc0MiA5Mi4xMjk2IDE5MC4wODIgOTIuMDcyMyAxOTAuNDU4IDkyLjA3MjNDMTkwLjc2MiA5Mi4wNzIzIDE5MS4wNDIgOTIuMTA5OSAxOTEuMjk2IDkyLjE4NTFDMTkxLjU1NCA5Mi4yNTY3IDE5MS43ODMgOTIuMzczIDE5MS45ODMgOTIuNTM0MkMxOTIuMTg0IDkyLjY5MTcgMTkyLjM1NCA5Mi45MDMgMTkyLjQ5NCA5My4xNjhDMTkyLjYzNyA5My40Mjk0IDE5Mi43NDYgOTMuNzQ5OCAxOTIuODIxIDk0LjEyOTRDMTkyLjg5NiA5NC41MDkgMTkyLjkzNCA5NC45NTQ4IDE5Mi45MzQgOTUuNDY2OFpNMTkxLjkzNSA5Ni44MjAzVjk1LjMwMDNDMTkxLjkzNSA5NC45NDk0IDE5MS45MTQgOTQuNjQxNCAxOTEuODcxIDk0LjM3NjVDMTkxLjgzMSA5NC4xMDc5IDE5MS43NzIgOTMuODc4NyAxOTEuNjkzIDkzLjY4OUMxOTEuNjE1IDkzLjQ5OTIgMTkxLjUxNCA5My4zNDUyIDE5MS4zOTMgOTMuMjI3MUMxOTEuMjc0IDkzLjEwODkgMTkxLjEzNyA5My4wMjI5IDE5MC45NzkgOTIuOTY5MkMxOTAuODI1IDkyLjkxMTkgMTkwLjY1MSA5Mi44ODMzIDE5MC40NTggOTIuODgzM0MxOTAuMjIyIDkyLjg4MzMgMTkwLjAxMiA5Mi45MjgxIDE4OS44MyA5My4wMTc2QzE4OS42NDcgOTMuMTAzNSAxODkuNDkzIDkzLjI0MTQgMTg5LjM2OCA5My40MzEyQzE4OS4yNDYgOTMuNjIwOSAxODkuMTUzIDkzLjg2OTggMTg5LjA4OCA5NC4xNzc3QzE4OS4wMjQgOTQuNDg1NyAxODguOTkyIDk0Ljg1OTkgMTg4Ljk5MiA5NS4zMDAzVjk2LjgyMDNDMTg4Ljk5MiA5Ny4xNzEyIDE4OS4wMTEgOTcuNDgxIDE4OS4wNTEgOTcuNzQ5NUMxODkuMDk0IDk4LjAxODEgMTg5LjE1NiA5OC4yNTA4IDE4OS4yMzkgOTguNDQ3OEMxODkuMzIxIDk4LjY0MTEgMTg5LjQyMSA5OC44MDA1IDE4OS41NCA5OC45MjU4QzE4OS42NTggOTkuMDUxMSAxODkuNzk0IDk5LjE0NDIgMTg5Ljk0OCA5OS4yMDUxQzE5MC4xMDUgOTkuMjYyNCAxOTAuMjc5IDk5LjI5MSAxOTAuNDY5IDk5LjI5MUMxOTAuNzEyIDk5LjI5MSAxOTAuOTI1IDk5LjI0NDUgMTkxLjEwOCA5OS4xNTE0QzE5MS4yOTEgOTkuMDU4MyAxOTEuNDQzIDk4LjkxMzIgMTkxLjU2NCA5OC43MTYzQzE5MS42OSA5OC41MTU4IDE5MS43ODMgOTguMjU5OCAxOTEuODQ0IDk3Ljk0ODJDMTkxLjkwNSA5Ny42MzMxIDE5MS45MzUgOTcuMjU3MiAxOTEuOTM1IDk2LjgyMDNaTTE5OS4zNzIgOTUuNDY2OFY5Ni42NTkyQzE5OS4zNzIgOTcuMzAwMSAxOTkuMzE0IDk3Ljg0MDggMTk5LjIgOTguMjgxMkMxOTkuMDg1IDk4LjcyMTcgMTk4LjkyIDk5LjA3NjIgMTk4LjcwNiA5OS4zNDQ3QzE5OC40OTEgOTkuNjEzMyAxOTguMjMxIDk5LjgwODQgMTk3LjkyNyA5OS45MzAyQzE5Ny42MjYgMTAwLjA0OCAxOTcuMjg2IDEwMC4xMDcgMTk2LjkwNiAxMDAuMTA3QzE5Ni42MDUgMTAwLjEwNyAxOTYuMzI4IDEwMC4wNyAxOTYuMDc0IDk5Ljk5NDZDMTk1LjgxOSA5OS45MTk0IDE5NS41OSA5OS43OTk1IDE5NS4zODYgOTkuNjM0OEMxOTUuMTg2IDk5LjQ2NjUgMTk1LjAxNCA5OS4yNDggMTk0Ljg3MSA5OC45Nzk1QzE5NC43MjcgOTguNzEwOSAxOTQuNjE4IDk4LjM4NTEgMTk0LjU0MyA5OC4wMDJDMTk0LjQ2OCA5Ny42MTg4IDE5NC40MyA5Ny4xNzEyIDE5NC40MyA5Ni42NTkyVjk1LjQ2NjhDMTk0LjQzIDk0LjgyNTggMTk0LjQ4NyA5NC4yODg3IDE5NC42MDIgOTMuODU1NUMxOTQuNzIgOTMuNDIyMiAxOTQuODg3IDkzLjA3NDkgMTk1LjEwMiA5Mi44MTM1QzE5NS4zMTYgOTIuNTQ4NSAxOTUuNTc0IDkyLjM1ODcgMTk1Ljg3NSA5Mi4yNDQxQzE5Ni4xNzkgOTIuMTI5NiAxOTYuNTIgOTIuMDcyMyAxOTYuODk2IDkyLjA3MjNDMTk3LjIgOTIuMDcyMyAxOTcuNDc5IDkyLjEwOTkgMTk3LjczMyA5Mi4xODUxQzE5Ny45OTEgOTIuMjU2NyAxOTguMjIgOTIuMzczIDE5OC40MjEgOTIuNTM0MkMxOTguNjIxIDkyLjY5MTcgMTk4Ljc5MiA5Mi45MDMgMTk4LjkzMSA5My4xNjhDMTk5LjA3NCA5My40Mjk0IDE5OS4xODQgOTMuNzQ5OCAxOTkuMjU5IDk0LjEyOTRDMTk5LjMzNCA5NC41MDkgMTk5LjM3MiA5NC45NTQ4IDE5OS4zNzIgOTUuNDY2OFpNMTk4LjM3MyA5Ni44MjAzVjk1LjMwMDNDMTk4LjM3MyA5NC45NDk0IDE5OC4zNTEgOTQuNjQxNCAxOTguMzA4IDk0LjM3NjVDMTk4LjI2OSA5NC4xMDc5IDE5OC4yMSA5My44Nzg3IDE5OC4xMzEgOTMuNjg5QzE5OC4wNTIgOTMuNDk5MiAxOTcuOTUyIDkzLjM0NTIgMTk3LjgzIDkzLjIyNzFDMTk3LjcxMiA5My4xMDg5IDE5Ny41NzQgOTMuMDIyOSAxOTcuNDE3IDkyLjk2OTJDMTk3LjI2MyA5Mi45MTE5IDE5Ny4wODkgOTIuODgzMyAxOTYuODk2IDkyLjg4MzNDMTk2LjY1OSA5Mi44ODMzIDE5Ni40NSA5Mi45MjgxIDE5Ni4yNjcgOTMuMDE3NkMxOTYuMDg0IDkzLjEwMzUgMTk1LjkzMSA5My4yNDE0IDE5NS44MDUgOTMuNDMxMkMxOTUuNjgzIDkzLjYyMDkgMTk1LjU5IDkzLjg2OTggMTk1LjUyNiA5NC4xNzc3QzE5NS40NjEgOTQuNDg1NyAxOTUuNDI5IDk0Ljg1OTkgMTk1LjQyOSA5NS4zMDAzVjk2LjgyMDNDMTk1LjQyOSA5Ny4xNzEyIDE5NS40NDkgOTcuNDgxIDE5NS40ODggOTcuNzQ5NUMxOTUuNTMxIDk4LjAxODEgMTk1LjU5NCA5OC4yNTA4IDE5NS42NzYgOTguNDQ3OEMxOTUuNzU5IDk4LjY0MTEgMTk1Ljg1OSA5OC44MDA1IDE5NS45NzcgOTguOTI1OEMxOTYuMDk1IDk5LjA1MTEgMTk2LjIzMSA5OS4xNDQyIDE5Ni4zODUgOTkuMjA1MUMxOTYuNTQzIDk5LjI2MjQgMTk2LjcxNiA5OS4yOTEgMTk2LjkwNiA5OS4yOTFDMTk3LjE1IDk5LjI5MSAxOTcuMzYzIDk5LjI0NDUgMTk3LjU0NSA5OS4xNTE0QzE5Ny43MjggOTkuMDU4MyAxOTcuODggOTguOTEzMiAxOTguMDAyIDk4LjcxNjNDMTk4LjEyNyA5OC41MTU4IDE5OC4yMiA5OC4yNTk4IDE5OC4yODEgOTcuOTQ4MkMxOTguMzQyIDk3LjYzMzEgMTk4LjM3MyA5Ny4yNTcyIDE5OC4zNzMgOTYuODIwM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPGNpcmNsZSBjeD0iMTAwIiBjeT0iODMiIHI9IjgiIGZpbGw9IiM1NDY5RkYiLz4KPC9zdmc+Cg==", + "description": "Allows users to move thumb of a slider to send commands to devices or update attributes/time-series data. Configurable settings let users define how to retrieve the initial state and specify actions for the value change.", + "descriptor": { + "type": "rpc", + "sizeX": 5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '360px',\n previewHeight: '220px',\n embedTitlePanel: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-slider-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-slider-basic-config", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{},\"title\":\"Slider\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"actions\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":null,\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"configMode\":\"basic\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request", + "update attribute", + "set attribute", + "add time-series", + "slider" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 0ddfc98705..4ccee86d5e 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -2317,7 +2317,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "45e4507d-3adc-bb31-8b2b-1ba09bbd56ac" @@ -2484,7 +2484,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "852eccce-98eb-24db-c783-bdd62566f906" @@ -2650,7 +2650,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "3c31ba62-e760-2bea-4c8d-d32784a86c24" @@ -2816,7 +2816,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "4b55ea81-93bf-4206-9166-3e0bdc1dd9f3" @@ -2982,7 +2982,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "babf88d0-a118-e2b5-f10e-3a5970c8a65b" @@ -3148,7 +3148,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "94de7690-f91d-b032-6771-85af99abd749" @@ -3314,7 +3314,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "18414f44-1c65-536a-14de-eaf21a7d56bd" @@ -3480,7 +3480,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "794974da-c9d2-a9f7-be47-c9eb642094e8" @@ -3646,7 +3646,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "2add705b-3e53-8559-8126-380cac686fb0" @@ -3812,7 +3812,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "7e1ba820-9992-d52a-579b-20485abb3926" @@ -3978,7 +3978,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "91af27c1-b37c-2276-6022-a332e41b2b33" @@ -4144,7 +4144,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "26cf8696-054b-13ec-7984-6fc5df20e6f1" @@ -4310,7 +4310,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "1dcfaf24-32be-cd19-62d6-86d12cc6a7ef" @@ -4476,7 +4476,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "ad2bc817-f3c4-150c-4672-8fe0c38aee8d" @@ -4642,7 +4642,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "d1ad84cd-bd9c-4dca-e4a0-f444ae8598bd" @@ -4808,7 +4808,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "bf80eef9-b879-9a08-40a4-488dbdefa125" @@ -4974,7 +4974,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "b5a406b3-cc0a-8a09-9aec-3f8befae5fb8" @@ -5140,7 +5140,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "custom", - "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", "openInSeparateDialog": false, "openInPopover": false, "id": "ec1dfba3-4b43-2491-8948-f602337f8a3b" diff --git a/application/src/main/data/upgrade/1.3.0/schema_update.cql b/application/src/main/data/upgrade/1.3.0/schema_update.cql deleted file mode 100644 index d28e9f689a..0000000000 --- a/application/src/main/data/upgrade/1.3.0/schema_update.cql +++ /dev/null @@ -1,187 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_and_name; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_tenant_by_type_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_customer_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_by_customer_by_type_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.device_types_by_tenant; - -DROP TABLE IF EXISTS thingsboard.device; - -CREATE TABLE IF NOT EXISTS thingsboard.device ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - type text, - search_text text, - additional_info text, - PRIMARY KEY (id, tenant_id, customer_id, type) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id, type) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_name; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_by_type_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_customer_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_customer_by_type_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_types_by_tenant; - -DROP TABLE IF EXISTS thingsboard.asset; - -CREATE TABLE IF NOT EXISTS thingsboard.asset ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - type text, - search_text text, - additional_info text, - PRIMARY KEY (id, tenant_id, customer_id, type) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id, type) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); - -CREATE TABLE IF NOT EXISTS thingsboard.entity_subtype ( - tenant_id timeuuid, - entity_type text, // (DEVICE, ASSET) - type text, - PRIMARY KEY (tenant_id, entity_type, type) -); - -CREATE TABLE IF NOT EXISTS thingsboard.alarm ( - id timeuuid, - tenant_id timeuuid, - type text, - originator_id timeuuid, - originator_type text, - severity text, - status text, - start_ts bigint, - end_ts bigint, - ack_ts bigint, - clear_ts bigint, - details text, - propagate boolean, - PRIMARY KEY ((tenant_id, originator_id, originator_type), type, id) -) WITH CLUSTERING ORDER BY ( type ASC, id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.alarm_by_id AS - SELECT * - from thingsboard.alarm - WHERE tenant_id IS NOT NULL AND originator_id IS NOT NULL AND originator_type IS NOT NULL AND type IS NOT NULL - AND type IS NOT NULL AND id IS NOT NULL - PRIMARY KEY (id, tenant_id, originator_id, originator_type, type) - WITH CLUSTERING ORDER BY ( tenant_id ASC, originator_id ASC, originator_type ASC, type ASC); - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.relation_by_type_and_child_type; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.reverse_relation; - -DROP TABLE IF EXISTS thingsboard.relation; - -CREATE TABLE IF NOT EXISTS thingsboard.relation ( - from_id timeuuid, - from_type text, - to_id timeuuid, - to_type text, - relation_type_group text, - relation_type text, - additional_info text, - PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_id, to_type) -) WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_id ASC, to_type ASC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.relation_by_type_and_child_type AS - SELECT * - from thingsboard.relation - WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL - PRIMARY KEY ((from_id, from_type), relation_type_group, relation_type, to_type, to_id) - WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, to_type ASC, to_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS - SELECT * - from thingsboard.relation - WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type_group IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL - PRIMARY KEY ((to_id, to_type), relation_type_group, relation_type, from_id, from_type) - WITH CLUSTERING ORDER BY ( relation_type_group ASC, relation_type ASC, from_id ASC, from_type ASC); diff --git a/application/src/main/data/upgrade/1.3.1/schema_update.sql b/application/src/main/data/upgrade/1.3.1/schema_update.sql deleted file mode 100644 index 73f37d457a..0000000000 --- a/application/src/main/data/upgrade/1.3.1/schema_update.sql +++ /dev/null @@ -1,17 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -ALTER TABLE ts_kv_latest ALTER COLUMN str_v SET DATA TYPE varchar(10000000); diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.cql b/application/src/main/data/upgrade/1.4.0/schema_update.cql deleted file mode 100644 index ba5ce1c4c7..0000000000 --- a/application/src/main/data/upgrade/1.4.0/schema_update.cql +++ /dev/null @@ -1,112 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id ( - tenant_id timeuuid, - id timeuuid, - customer_id timeuuid, - entity_id timeuuid, - entity_type text, - entity_name text, - user_id timeuuid, - user_name text, - action_type text, - action_data text, - action_status text, - action_failure_details text, - PRIMARY KEY ((tenant_id, entity_id, entity_type), id) -); - -CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_customer_id ( - tenant_id timeuuid, - id timeuuid, - customer_id timeuuid, - entity_id timeuuid, - entity_type text, - entity_name text, - user_id timeuuid, - user_name text, - action_type text, - action_data text, - action_status text, - action_failure_details text, - PRIMARY KEY ((tenant_id, customer_id), id) -); - -CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id ( - tenant_id timeuuid, - id timeuuid, - customer_id timeuuid, - entity_id timeuuid, - entity_type text, - entity_name text, - user_id timeuuid, - user_name text, - action_type text, - action_data text, - action_status text, - action_failure_details text, - PRIMARY KEY ((tenant_id, user_id), id) -); - - - -CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id ( - tenant_id timeuuid, - id timeuuid, - partition bigint, - customer_id timeuuid, - entity_id timeuuid, - entity_type text, - entity_name text, - user_id timeuuid, - user_name text, - action_type text, - action_data text, - action_status text, - action_failure_details text, - PRIMARY KEY ((tenant_id, partition), id) -); - -CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( - tenant_id timeuuid, - partition bigint, - PRIMARY KEY (( tenant_id ), partition) -) WITH CLUSTERING ORDER BY ( partition ASC ) -AND compaction = { 'class' : 'LeveledCompactionStrategy' }; - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_tenant_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_customer_and_search_text; - -DROP TABLE IF EXISTS thingsboard.dashboard; - -CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( - id timeuuid, - tenant_id timeuuid, - title text, - search_text text, - assigned_customers text, - configuration text, - PRIMARY KEY (id, tenant_id) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS - SELECT * - from thingsboard.dashboard - WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); - diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.sql b/application/src/main/data/upgrade/1.4.0/schema_update.sql deleted file mode 100644 index 9c24ba95cf..0000000000 --- a/application/src/main/data/upgrade/1.4.0/schema_update.sql +++ /dev/null @@ -1,41 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS audit_log ( - id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY, - tenant_id varchar(31), - customer_id varchar(31), - entity_id varchar(31), - entity_type varchar(255), - entity_name varchar(255), - user_id varchar(31), - user_name varchar(255), - action_type varchar(255), - action_data varchar(1000000), - action_status varchar(255), - action_failure_details varchar(1000000) -); - -DROP TABLE IF EXISTS dashboard; - -CREATE TABLE IF NOT EXISTS dashboard ( - id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, - configuration varchar(10000000), - assigned_customers varchar(1000000), - search_text varchar(255), - tenant_id varchar(31), - title varchar(255) -); diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.cql b/application/src/main/data/upgrade/2.0.0/schema_update.cql deleted file mode 100644 index 843e81f2f9..0000000000 --- a/application/src/main/data/upgrade/2.0.0/schema_update.cql +++ /dev/null @@ -1,103 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS thingsboard.msg_queue ( - node_id timeuuid, - cluster_partition bigint, - ts_partition bigint, - ts bigint, - msg blob, - PRIMARY KEY ((node_id, cluster_partition, ts_partition), ts)) -WITH CLUSTERING ORDER BY (ts DESC) -AND compaction = { - 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', - 'min_threshold': '5', - 'base_time_seconds': '43200', - 'max_window_size_seconds': '43200', - 'tombstone_threshold': '0.9', - 'unchecked_tombstone_compaction': 'true' -}; - -CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue ( - node_id timeuuid, - cluster_partition bigint, - ts_partition bigint, - msg_id timeuuid, - PRIMARY KEY ((node_id, cluster_partition, ts_partition), msg_id)) -WITH CLUSTERING ORDER BY (msg_id DESC) -AND compaction = { - 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', - 'min_threshold': '5', - 'base_time_seconds': '43200', - 'max_window_size_seconds': '43200', - 'tombstone_threshold': '0.9', - 'unchecked_tombstone_compaction': 'true' -}; - -CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions ( - node_id timeuuid, - cluster_partition bigint, - ts_partition bigint, - PRIMARY KEY ((node_id, cluster_partition), ts_partition)) -WITH CLUSTERING ORDER BY (ts_partition DESC) -AND compaction = { - 'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy', - 'min_threshold': '5', - 'base_time_seconds': '43200', - 'max_window_size_seconds': '43200', - 'tombstone_threshold': '0.9', - 'unchecked_tombstone_compaction': 'true' -}; - -CREATE TABLE IF NOT EXISTS thingsboard.rule_chain ( - id uuid, - tenant_id uuid, - name text, - search_text text, - first_rule_node_id uuid, - root boolean, - debug_mode boolean, - configuration text, - additional_info text, - PRIMARY KEY (id, tenant_id) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS - SELECT * - from thingsboard.rule_chain - WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); - -CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( - id uuid, - rule_chain_id uuid, - type text, - name text, - debug_mode boolean, - search_text text, - configuration text, - additional_info text, - PRIMARY KEY (id) -); - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_plugin_token; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_by_tenant_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_api_token; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.plugin_by_tenant_and_search_text; - -DROP TABLE IF EXISTS thingsboard.rule; -DROP TABLE IF EXISTS thingsboard.plugin; diff --git a/application/src/main/data/upgrade/2.0.0/schema_update.sql b/application/src/main/data/upgrade/2.0.0/schema_update.sql deleted file mode 100644 index 5395d9a160..0000000000 --- a/application/src/main/data/upgrade/2.0.0/schema_update.sql +++ /dev/null @@ -1,44 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS rule_chain ( - id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, - additional_info varchar, - configuration varchar(10000000), - name varchar(255), - first_rule_node_id varchar(31), - root boolean, - debug_mode boolean, - search_text varchar(255), - tenant_id varchar(31) -); - -CREATE TABLE IF NOT EXISTS rule_node ( - id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, - rule_chain_id varchar(31), - additional_info varchar, - configuration varchar(10000000), - type varchar(255), - name varchar(255), - debug_mode boolean, - search_text varchar(255) -); - -DROP TABLE rule; -DROP TABLE plugin; - -DELETE FROM alarm WHERE originator_type = 3 OR originator_type = 4; -UPDATE alarm SET originator_type = (originator_type - 2) where originator_type > 2; diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql deleted file mode 100644 index ad5c0a13ac..0000000000 --- a/application/src/main/data/upgrade/2.1.1/schema_update.cql +++ /dev/null @@ -1,74 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( - id timeuuid, - entity_id timeuuid, - entity_type text, - tenant_id timeuuid, - customer_id timeuuid, - name text, - keys text, - start_ts bigint, - end_ts bigint, - search_text text, - additional_info text, - PRIMARY KEY (id, entity_id, tenant_id, customer_id) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS - SELECT * - from thingsboard.entity_views - WHERE tenant_id IS NOT NULL - AND entity_id IS NOT NULL - AND customer_id IS NOT NULL - AND name IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, name, id, customer_id, entity_id) - WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS - SELECT * - from thingsboard.entity_views - WHERE tenant_id IS NOT NULL - AND entity_id IS NOT NULL - AND customer_id IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id) - WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS - SELECT * - from thingsboard.entity_views - WHERE tenant_id IS NOT NULL - AND customer_id IS NOT NULL - AND entity_id IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) - WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS - SELECT * - from thingsboard.entity_views - WHERE tenant_id IS NOT NULL - AND customer_id IS NOT NULL - AND entity_id IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id) - WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.sql b/application/src/main/data/upgrade/2.1.1/schema_update.sql deleted file mode 100644 index 91d0b3bcb6..0000000000 --- a/application/src/main/data/upgrade/2.1.1/schema_update.sql +++ /dev/null @@ -1,29 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS entity_views ( - id varchar(31) NOT NULL CONSTRAINT entity_views_pkey PRIMARY KEY, - entity_id varchar(31), - entity_type varchar(255), - tenant_id varchar(31), - customer_id varchar(31), - name varchar(255), - keys varchar(255), - start_ts bigint, - end_ts bigint, - search_text varchar(255), - additional_info varchar -); diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.cql b/application/src/main/data/upgrade/2.1.2/schema_update.cql deleted file mode 100644 index 0fcc6ddbc9..0000000000 --- a/application/src/main/data/upgrade/2.1.2/schema_update.cql +++ /dev/null @@ -1,110 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer; -DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id; - -DROP TABLE IF EXISTS thingsboard.entity_views; - -CREATE TABLE IF NOT EXISTS thingsboard.entity_view ( - id timeuuid, - entity_id timeuuid, - entity_type text, - tenant_id timeuuid, - customer_id timeuuid, - name text, - type text, - keys text, - start_ts bigint, - end_ts bigint, - search_text text, - additional_info text, - PRIMARY KEY (id, entity_id, tenant_id, customer_id, type) -); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND entity_id IS NOT NULL - AND customer_id IS NOT NULL - AND type IS NOT NULL - AND name IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type) - WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND entity_id IS NOT NULL - AND customer_id IS NOT NULL - AND type IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type) - WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND entity_id IS NOT NULL - AND customer_id IS NOT NULL - AND type IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id) - WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND customer_id IS NOT NULL - AND entity_id IS NOT NULL - AND type IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type) - WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND customer_id IS NOT NULL - AND entity_id IS NOT NULL - AND type IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id) - WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS - SELECT * - from thingsboard.entity_view - WHERE tenant_id IS NOT NULL - AND customer_id IS NOT NULL - AND entity_id IS NOT NULL - AND type IS NOT NULL - AND search_text IS NOT NULL - AND id IS NOT NULL - PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) - WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.sql b/application/src/main/data/upgrade/2.1.2/schema_update.sql deleted file mode 100644 index 744edb8834..0000000000 --- a/application/src/main/data/upgrade/2.1.2/schema_update.sql +++ /dev/null @@ -1,32 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP TABLE IF EXISTS entity_views; - -CREATE TABLE IF NOT EXISTS entity_view ( - id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, - entity_id varchar(31), - entity_type varchar(255), - tenant_id varchar(31), - customer_id varchar(31), - type varchar(255), - name varchar(255), - keys varchar(255), - start_ts bigint, - end_ts bigint, - search_text varchar(255), - additional_info varchar -); diff --git a/application/src/main/data/upgrade/2.2.0/schema_update.sql b/application/src/main/data/upgrade/2.2.0/schema_update.sql deleted file mode 100644 index 67b648a2e3..0000000000 --- a/application/src/main/data/upgrade/2.2.0/schema_update.sql +++ /dev/null @@ -1,19 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -ALTER TABLE component_descriptor ADD UNIQUE (clazz); - -ALTER TABLE entity_view ALTER COLUMN keys SET DATA TYPE varchar(10000000); diff --git a/application/src/main/data/upgrade/2.3.1/schema_update.sql b/application/src/main/data/upgrade/2.3.1/schema_update.sql deleted file mode 100644 index a7929e2e35..0000000000 --- a/application/src/main/data/upgrade/2.3.1/schema_update.sql +++ /dev/null @@ -1,17 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -ALTER TABLE event ALTER COLUMN body SET DATA TYPE varchar(10000000); diff --git a/application/src/main/data/upgrade/2.4.0/schema_update.sql b/application/src/main/data/upgrade/2.4.0/schema_update.sql deleted file mode 100644 index 5e95b8c8de..0000000000 --- a/application/src/main/data/upgrade/2.4.0/schema_update.sql +++ /dev/null @@ -1,23 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(tenant_id, type, originator_type, originator_id); - -CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); - -CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); - -CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id); diff --git a/application/src/main/data/upgrade/2.4.2/schema_update.sql b/application/src/main/data/upgrade/2.4.2/schema_update.sql deleted file mode 100644 index ab1d9201c4..0000000000 --- a/application/src/main/data/upgrade/2.4.2/schema_update.sql +++ /dev/null @@ -1,31 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; - -CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); - -CREATE INDEX IF NOT EXISTS idx_device_customer_id ON device(tenant_id, customer_id); - -CREATE INDEX IF NOT EXISTS idx_device_customer_id_and_type ON device(tenant_id, customer_id, type); - -CREATE INDEX IF NOT EXISTS idx_device_type ON device(tenant_id, type); - -CREATE INDEX IF NOT EXISTS idx_asset_customer_id ON asset(tenant_id, customer_id); - -CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, customer_id, type); - -CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql deleted file mode 100644 index 4e91bfb367..0000000000 --- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql +++ /dev/null @@ -1,209 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - max_tenant_ttl bigint; - max_customer_ttl bigint; - max_ttl bigint; - date timestamp; - partition_by_max_ttl_date varchar; - partition_by_max_ttl_month varchar; - partition_by_max_ttl_day varchar; - partition_by_max_ttl_year varchar; - partition varchar; - partition_year integer; - partition_month integer; - partition_day integer; - -BEGIN - SELECT max(attribute_kv.long_v) - FROM tenant - INNER JOIN attribute_kv ON tenant.id = attribute_kv.entity_id - WHERE attribute_kv.attribute_key = 'TTL' - into max_tenant_ttl; - SELECT max(attribute_kv.long_v) - FROM customer - INNER JOIN attribute_kv ON customer.id = attribute_kv.entity_id - WHERE attribute_kv.attribute_key = 'TTL' - into max_customer_ttl; - max_ttl := GREATEST(system_ttl, max_customer_ttl, max_tenant_ttl); - if max_ttl IS NOT NULL AND max_ttl > 0 THEN - date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); - partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); - RAISE NOTICE 'Date by max ttl: %', date; - RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; - IF partition_by_max_ttl_date IS NOT NULL THEN - CASE - WHEN partition_type = 'DAYS' THEN - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); - WHEN partition_type = 'MONTHS' THEN - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - ELSE - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - END CASE; - IF partition_by_max_ttl_year IS NULL THEN - RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; - ELSE - IF partition_type = 'YEARS' THEN - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END LOOP; - ELSE - IF partition_type = 'MONTHS' THEN - IF partition_by_max_ttl_month IS NULL THEN - RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; - ELSE - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year > partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_month := SPLIT_PART(partition, '_', 4)::integer; - IF partition_year = partition_by_max_ttl_year::integer THEN - IF partition_month >= partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END IF; - END IF; - END IF; - END LOOP; - END IF; - ELSE - IF partition_type = 'DAYS' THEN - IF partition_by_max_ttl_month IS NULL THEN - RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; - ELSE - IF partition_by_max_ttl_day IS NULL THEN - RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; - ELSE - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year > partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_month := SPLIT_PART(partition, '_', 4)::integer; - IF partition_month > partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_month < partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_day := SPLIT_PART(partition, '_', 5)::integer; - IF partition_day >= partition_by_max_ttl_day::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_day < partition_by_max_ttl_day::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END LOOP; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; -END -$$; - -CREATE OR REPLACE FUNCTION get_partition_by_max_ttl_date(IN partition_type varchar, IN date timestamp, OUT partition varchar) AS -$$ -BEGIN - CASE - WHEN partition_type = 'DAYS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM') || '_' || to_char(date, 'dd'); - WHEN partition_type = 'MONTHS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); - WHEN partition_type = 'YEARS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy'); - ELSE - partition := NULL; - END CASE; - IF partition IS NOT NULL THEN - IF NOT EXISTS(SELECT - FROM pg_tables - WHERE schemaname = 'public' - AND tablename = partition) THEN - partition := NULL; - RAISE NOTICE 'Failed to found partition by ttl'; - END IF; - END IF; -END; -$$ LANGUAGE plpgsql; diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql deleted file mode 100644 index 0883e46982..0000000000 --- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_ts.sql +++ /dev/null @@ -1,359 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - --- call create_partition_ts_kv_table(); - -CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() - LANGUAGE plpgsql AS -$$ - -BEGIN - ALTER TABLE ts_kv - DROP CONSTRAINT IF EXISTS ts_kv_unq_key; - ALTER TABLE ts_kv - DROP CONSTRAINT IF EXISTS ts_kv_pkey; - ALTER TABLE ts_kv - ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_type, entity_id, key, ts); - ALTER TABLE ts_kv - RENAME TO ts_kv_old; - ALTER TABLE ts_kv_old - RENAME CONSTRAINT ts_kv_pkey TO ts_kv_pkey_old; - CREATE TABLE IF NOT EXISTS ts_kv - ( - LIKE ts_kv_old - ) - PARTITION BY RANGE (ts); - ALTER TABLE ts_kv - DROP COLUMN entity_type; - ALTER TABLE ts_kv - ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid; - ALTER TABLE ts_kv - ALTER COLUMN key TYPE integer USING key::integer; - ALTER TABLE ts_kv - ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts); - CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT; -END; -$$; - --- call create_new_ts_kv_latest_table(); - -CREATE OR REPLACE PROCEDURE create_new_ts_kv_latest_table() - LANGUAGE plpgsql AS -$$ - -BEGIN - IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest_old') THEN - ALTER TABLE ts_kv_latest - DROP CONSTRAINT IF EXISTS ts_kv_latest_unq_key; - ALTER TABLE ts_kv_latest - DROP CONSTRAINT IF EXISTS ts_kv_latest_pkey; - ALTER TABLE ts_kv_latest - ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key); - ALTER TABLE ts_kv_latest - RENAME TO ts_kv_latest_old; - ALTER TABLE ts_kv_latest_old - RENAME CONSTRAINT ts_kv_latest_pkey TO ts_kv_latest_pkey_old; - CREATE TABLE IF NOT EXISTS ts_kv_latest - ( - LIKE ts_kv_latest_old - ); - ALTER TABLE ts_kv_latest - DROP COLUMN entity_type; - ALTER TABLE ts_kv_latest - ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid; - ALTER TABLE ts_kv_latest - ALTER COLUMN key TYPE integer USING key::integer; - ALTER TABLE ts_kv_latest - ADD CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key); - ELSE - RAISE NOTICE 'ts_kv_latest_old table already exists!'; - IF NOT EXISTS(SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'ts_kv_latest') THEN - CREATE TABLE IF NOT EXISTS ts_kv_latest - ( - entity_id uuid NOT NULL, - key int NOT NULL, - ts bigint NOT NULL, - bool_v boolean, - str_v varchar(10000000), - long_v bigint, - dbl_v double precision, - json_v json, - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) - ); - END IF; - END IF; -END; -$$; - -CREATE OR REPLACE FUNCTION get_partitions_data(IN partition_type varchar) - RETURNS - TABLE - ( - partition_date text, - from_ts bigint, - to_ts bigint - ) -AS -$$ -BEGIN - CASE - WHEN partition_type = 'DAYS' THEN - RETURN QUERY SELECT day_date.day AS partition_date, - (extract(epoch from (day_date.day)::timestamp) * 1000)::bigint AS from_ts, - (extract(epoch from (day_date.day::date + INTERVAL '1 DAY')::timestamp) * - 1000)::bigint AS to_ts - FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_MM_DD') AS day - FROM ts_kv_old) AS day_date; - WHEN partition_type = 'MONTHS' THEN - RETURN QUERY SELECT SUBSTRING(month_date.first_date, 1, 7) AS partition_date, - (extract(epoch from (month_date.first_date)::timestamp) * 1000)::bigint AS from_ts, - (extract(epoch from (month_date.first_date::date + INTERVAL '1 MONTH')::timestamp) * - 1000)::bigint AS to_ts - FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_MM_01') AS first_date - FROM ts_kv_old) AS month_date; - WHEN partition_type = 'YEARS' THEN - RETURN QUERY SELECT SUBSTRING(year_date.year, 1, 4) AS partition_date, - (extract(epoch from (year_date.year)::timestamp) * 1000)::bigint AS from_ts, - (extract(epoch from (year_date.year::date + INTERVAL '1 YEAR')::timestamp) * - 1000)::bigint AS to_ts - FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_01_01') AS year - FROM ts_kv_old) AS year_date; - ELSE - RAISE EXCEPTION 'Failed to parse partitioning property: % !', partition_type; - END CASE; -END; -$$ LANGUAGE plpgsql; - --- call create_partitions(); - -CREATE OR REPLACE PROCEDURE create_partitions(IN partition_type varchar) - LANGUAGE plpgsql AS -$$ - -DECLARE - partition_date varchar; - from_ts bigint; - to_ts bigint; - partitions_cursor CURSOR FOR SELECT * - FROM get_partitions_data(partition_type); -BEGIN - OPEN partitions_cursor; - LOOP - FETCH partitions_cursor INTO partition_date, from_ts, to_ts; - EXIT WHEN NOT FOUND; - EXECUTE 'CREATE TABLE IF NOT EXISTS ts_kv_' || partition_date || - ' PARTITION OF ts_kv FOR VALUES FROM (' || from_ts || - ') TO (' || to_ts || ');'; - RAISE NOTICE 'A partition % has been created!',CONCAT('ts_kv_', partition_date); - END LOOP; - - CLOSE partitions_cursor; -END; -$$; - --- call create_ts_kv_dictionary_table(); - -CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table() - LANGUAGE plpgsql AS -$$ - -BEGIN - CREATE TABLE IF NOT EXISTS ts_kv_dictionary - ( - key varchar(255) NOT NULL, - key_id serial UNIQUE, - CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) - ); -END; -$$; - --- call insert_into_dictionary(); - -CREATE OR REPLACE PROCEDURE insert_into_dictionary() - LANGUAGE plpgsql AS -$$ - -DECLARE - insert_record RECORD; - key_cursor CURSOR FOR SELECT DISTINCT key - FROM ts_kv_old - ORDER BY key; -BEGIN - OPEN key_cursor; - LOOP - FETCH key_cursor INTO insert_record; - EXIT WHEN NOT FOUND; - IF NOT EXISTS(SELECT key FROM ts_kv_dictionary WHERE key = insert_record.key) THEN - INSERT INTO ts_kv_dictionary(key) VALUES (insert_record.key); - RAISE NOTICE 'Key: % has been inserted into the dictionary!',insert_record.key; - ELSE - RAISE NOTICE 'Key: % already exists in the dictionary!',insert_record.key; - END IF; - END LOOP; - CLOSE key_cursor; -END; -$$; - -CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS -$$ -BEGIN - uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) || - '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12); -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar) - LANGUAGE plpgsql AS -$$ -BEGIN - EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id, - ts_kv_records.key AS key, - ts_kv_records.ts AS ts, - ts_kv_records.bool_v AS bool_v, - ts_kv_records.str_v AS str_v, - ts_kv_records.long_v AS long_v, - ts_kv_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM ts_kv_old - INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records) TO %L;', - path_to_file); - EXECUTE format('COPY ts_kv FROM %L', path_to_file); -END -$$; - --- call insert_into_ts_kv_latest(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest(IN path_to_file varchar) - LANGUAGE plpgsql AS -$$ -BEGIN - EXECUTE format('COPY (SELECT to_uuid(entity_id) AS entity_id, - ts_kv_latest_records.key AS key, - ts_kv_latest_records.ts AS ts, - ts_kv_latest_records.bool_v AS bool_v, - ts_kv_latest_records.str_v AS str_v, - ts_kv_latest_records.long_v AS long_v, - ts_kv_latest_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM ts_kv_latest_old - INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records) TO %L;', - path_to_file); - EXECUTE format('COPY ts_kv_latest FROM %L', path_to_file); -END; -$$; - --- call insert_into_ts_kv_cursor(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor() - LANGUAGE plpgsql AS -$$ -DECLARE - insert_size CONSTANT integer := 10000; - insert_counter integer DEFAULT 0; - insert_record RECORD; - insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id, - ts_kv_records.key AS key, - ts_kv_records.ts AS ts, - ts_kv_records.bool_v AS bool_v, - ts_kv_records.str_v AS str_v, - ts_kv_records.long_v AS long_v, - ts_kv_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM ts_kv_old - INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS ts_kv_records; -BEGIN - OPEN insert_cursor; - LOOP - insert_counter := insert_counter + 1; - FETCH insert_cursor INTO insert_record; - IF NOT FOUND THEN - RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter - 1; - EXIT; - END IF; - INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v) - VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v, - insert_record.long_v, insert_record.dbl_v); - IF MOD(insert_counter, insert_size) = 0 THEN - RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter; - END IF; - END LOOP; - CLOSE insert_cursor; -END; -$$; - --- call insert_into_ts_kv_latest_cursor(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest_cursor() - LANGUAGE plpgsql AS -$$ -DECLARE - insert_size CONSTANT integer := 10000; - insert_counter integer DEFAULT 0; - insert_record RECORD; - insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id, - ts_kv_latest_records.key AS key, - ts_kv_latest_records.ts AS ts, - ts_kv_latest_records.bool_v AS bool_v, - ts_kv_latest_records.str_v AS str_v, - ts_kv_latest_records.long_v AS long_v, - ts_kv_latest_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM ts_kv_latest_old - INNER JOIN ts_kv_dictionary ON (ts_kv_latest_old.key = ts_kv_dictionary.key)) AS ts_kv_latest_records; -BEGIN - OPEN insert_cursor; - LOOP - insert_counter := insert_counter + 1; - FETCH insert_cursor INTO insert_record; - IF NOT FOUND THEN - RAISE NOTICE '% records have been inserted into the ts_kv_latest!',insert_counter - 1; - EXIT; - END IF; - INSERT INTO ts_kv_latest(entity_id, key, ts, bool_v, str_v, long_v, dbl_v) - VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v, - insert_record.long_v, insert_record.dbl_v); - IF MOD(insert_counter, insert_size) = 0 THEN - RAISE NOTICE '% records have been inserted into the ts_kv_latest!',insert_counter; - END IF; - END LOOP; - CLOSE insert_cursor; -END; -$$; - diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql b/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql deleted file mode 100644 index 3e86854a00..0000000000 --- a/application/src/main/data/upgrade/2.4.3/schema_update_timescale_ts.sql +++ /dev/null @@ -1,208 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - --- call create_new_ts_kv_table(); - -CREATE OR REPLACE PROCEDURE create_new_ts_kv_table() LANGUAGE plpgsql AS $$ - -BEGIN - ALTER TABLE tenant_ts_kv - RENAME TO tenant_ts_kv_old; - CREATE TABLE IF NOT EXISTS ts_kv - ( - LIKE tenant_ts_kv_old - ); - ALTER TABLE ts_kv ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid; - ALTER TABLE ts_kv ALTER COLUMN key TYPE integer USING key::integer; - ALTER INDEX ts_kv_pkey RENAME TO tenant_ts_kv_pkey_old; - ALTER INDEX idx_tenant_ts_kv RENAME TO idx_tenant_ts_kv_old; - ALTER INDEX tenant_ts_kv_ts_idx RENAME TO tenant_ts_kv_ts_idx_old; - ALTER TABLE ts_kv ADD CONSTRAINT ts_kv_pkey PRIMARY KEY(entity_id, key, ts); --- CREATE INDEX IF NOT EXISTS ts_kv_ts_idx ON ts_kv(ts DESC); - ALTER TABLE ts_kv DROP COLUMN IF EXISTS tenant_id; -END; -$$; - - --- call create_ts_kv_latest_table(); - -CREATE OR REPLACE PROCEDURE create_ts_kv_latest_table() LANGUAGE plpgsql AS $$ - -BEGIN - CREATE TABLE IF NOT EXISTS ts_kv_latest - ( - entity_id uuid NOT NULL, - key int NOT NULL, - ts bigint NOT NULL, - bool_v boolean, - str_v varchar(10000000), - long_v bigint, - dbl_v double precision, - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) - ); -END; -$$; - - --- call create_ts_kv_dictionary_table(); - -CREATE OR REPLACE PROCEDURE create_ts_kv_dictionary_table() LANGUAGE plpgsql AS $$ - -BEGIN - CREATE TABLE IF NOT EXISTS ts_kv_dictionary - ( - key varchar(255) NOT NULL, - key_id serial UNIQUE, - CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) - ); -END; -$$; - --- call insert_into_dictionary(); - -CREATE OR REPLACE PROCEDURE insert_into_dictionary() LANGUAGE plpgsql AS $$ - -DECLARE - insert_record RECORD; - key_cursor CURSOR FOR SELECT DISTINCT key - FROM tenant_ts_kv_old - ORDER BY key; -BEGIN - OPEN key_cursor; - LOOP - FETCH key_cursor INTO insert_record; - EXIT WHEN NOT FOUND; - IF NOT EXISTS(SELECT key FROM ts_kv_dictionary WHERE key = insert_record.key) THEN - INSERT INTO ts_kv_dictionary(key) VALUES (insert_record.key); - RAISE NOTICE 'Key: % has been inserted into the dictionary!',insert_record.key; - ELSE - RAISE NOTICE 'Key: % already exists in the dictionary!',insert_record.key; - END IF; - END LOOP; - CLOSE key_cursor; -END; -$$; - -CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS -$$ -BEGIN - uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) || - '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12); -END; -$$ LANGUAGE plpgsql; - --- call insert_into_ts_kv(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv(IN path_to_file varchar) LANGUAGE plpgsql AS $$ -BEGIN - - EXECUTE format ('COPY (SELECT to_uuid(entity_id) AS entity_id, - new_ts_kv_records.key AS key, - new_ts_kv_records.ts AS ts, - new_ts_kv_records.bool_v AS bool_v, - new_ts_kv_records.str_v AS str_v, - new_ts_kv_records.long_v AS long_v, - new_ts_kv_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM tenant_ts_kv_old - INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records) TO %L;', path_to_file); - EXECUTE format ('COPY ts_kv FROM %L', path_to_file); -END; -$$; - --- call insert_into_ts_kv_latest(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_latest() LANGUAGE plpgsql AS $$ - -DECLARE - insert_size CONSTANT integer := 10000; - insert_counter integer DEFAULT 0; - latest_record RECORD; - insert_record RECORD; - insert_cursor CURSOR FOR SELECT - latest_records.key AS key, - latest_records.entity_id AS entity_id, - latest_records.ts AS ts - FROM (SELECT DISTINCT key AS key, entity_id AS entity_id, MAX(ts) AS ts FROM ts_kv GROUP BY key, entity_id) AS latest_records; -BEGIN - OPEN insert_cursor; - LOOP - insert_counter := insert_counter + 1; - FETCH insert_cursor INTO latest_record; - IF NOT FOUND THEN - RAISE NOTICE '% records have been inserted into the ts_kv_latest table!',insert_counter - 1; - EXIT; - END IF; - SELECT entity_id AS entity_id, key AS key, ts AS ts, bool_v AS bool_v, str_v AS str_v, long_v AS long_v, dbl_v AS dbl_v INTO insert_record FROM ts_kv WHERE entity_id = latest_record.entity_id AND key = latest_record.key AND ts = latest_record.ts; - INSERT INTO ts_kv_latest(entity_id, key, ts, bool_v, str_v, long_v, dbl_v) - VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v, insert_record.long_v, insert_record.dbl_v); - IF MOD(insert_counter, insert_size) = 0 THEN - RAISE NOTICE '% records have been inserted into the ts_kv_latest table!',insert_counter; - END IF; - END LOOP; - CLOSE insert_cursor; -END; -$$; - --- call insert_into_ts_kv_cursor(); - -CREATE OR REPLACE PROCEDURE insert_into_ts_kv_cursor() LANGUAGE plpgsql AS $$ - -DECLARE - insert_size CONSTANT integer := 10000; - insert_counter integer DEFAULT 0; - insert_record RECORD; - insert_cursor CURSOR FOR SELECT to_uuid(entity_id) AS entity_id, - new_ts_kv_records.key AS key, - new_ts_kv_records.ts AS ts, - new_ts_kv_records.bool_v AS bool_v, - new_ts_kv_records.str_v AS str_v, - new_ts_kv_records.long_v AS long_v, - new_ts_kv_records.dbl_v AS dbl_v - FROM (SELECT entity_id AS entity_id, - key_id AS key, - ts, - bool_v, - str_v, - long_v, - dbl_v - FROM tenant_ts_kv_old - INNER JOIN ts_kv_dictionary ON (tenant_ts_kv_old.key = ts_kv_dictionary.key)) AS new_ts_kv_records; -BEGIN - OPEN insert_cursor; - LOOP - insert_counter := insert_counter + 1; - FETCH insert_cursor INTO insert_record; - IF NOT FOUND THEN - RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter - 1; - EXIT; - END IF; - INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v) - VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v, - insert_record.long_v, insert_record.dbl_v); - IF MOD(insert_counter, insert_size) = 0 THEN - RAISE NOTICE '% records have been inserted into the new ts_kv table!',insert_counter; - END IF; - END LOOP; - CLOSE insert_cursor; -END; -$$; \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql b/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql deleted file mode 100644 index 53ab8351e4..0000000000 --- a/application/src/main/data/upgrade/2.4.3/schema_update_ttl.sql +++ /dev/null @@ -1,150 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS -$$ -BEGIN - uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) || - '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12); -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION delete_device_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint, - OUT deleted bigint) AS -$$ -BEGIN - EXECUTE format( - 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT device.id as entity_id FROM device WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', - tenant_id, customer_id, ttl) into deleted; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION delete_asset_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint, - OUT deleted bigint) AS -$$ -BEGIN - EXECUTE format( - 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT asset.id as entity_id FROM asset WHERE tenant_id = %L and customer_id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', - tenant_id, customer_id, ttl) into deleted; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION delete_customer_records_from_ts_kv(tenant_id uuid, customer_id uuid, ttl bigint, - OUT deleted bigint) AS -$$ -BEGIN - EXECUTE format( - 'WITH deleted AS (DELETE FROM ts_kv WHERE entity_id IN (SELECT customer.id as entity_id FROM customer WHERE tenant_id = %L and id = %L) AND ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', - tenant_id, customer_id, ttl) into deleted; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid, - IN system_ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - tenant_cursor CURSOR FOR select tenant.id as tenant_id - from tenant; - tenant_id_record uuid; - customer_id_record uuid; - tenant_ttl bigint; - customer_ttl bigint; - deleted_for_entities bigint; - tenant_ttl_ts bigint; - customer_ttl_ts bigint; -BEGIN - OPEN tenant_cursor; - FETCH tenant_cursor INTO tenant_id_record; - WHILE FOUND - LOOP - EXECUTE format( - 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', - tenant_id_record, 'TTL') INTO tenant_ttl; - if tenant_ttl IS NULL THEN - tenant_ttl := system_ttl; - END IF; - IF tenant_ttl > 0 THEN - tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint; - deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record; - deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record; - END IF; - FOR customer_id_record IN - SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record - LOOP - EXECUTE format( - 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', - customer_id_record, 'TTL') INTO customer_ttl; - IF customer_ttl IS NULL THEN - customer_ttl_ts := tenant_ttl_ts; - ELSE - IF customer_ttl > 0 THEN - customer_ttl_ts := - (EXTRACT(EPOCH FROM current_timestamp) * 1000 - - customer_ttl::bigint * 1000)::bigint; - END IF; - END IF; - IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN - deleted_for_entities := - delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record; - deleted_for_entities := - delete_device_records_from_ts_kv(tenant_id_record, customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; - deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, - customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; - END IF; - END LOOP; - FETCH tenant_cursor INTO tenant_id_record; - END LOOP; -END -$$; - -CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - ttl_ts bigint; - debug_ttl_ts bigint; - ttl_deleted_count bigint DEFAULT 0; - debug_ttl_deleted_count bigint DEFAULT 0; -BEGIN - IF ttl > 0 THEN - ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; - EXECUTE format( - 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type != %L::varchar AND event_type != %L::varchar) RETURNING *) SELECT count(*) FROM deleted', ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count; - END IF; - IF debug_ttl > 0 THEN - debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint; - EXECUTE format( - 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type = %L::varchar OR event_type = %L::varchar) RETURNING *) SELECT count(*) FROM deleted', debug_ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count; - END IF; - RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count; - RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count; - deleted := ttl_deleted_count + debug_ttl_deleted_count; -END -$$; diff --git a/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql b/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql deleted file mode 100644 index d4697026d5..0000000000 --- a/application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql +++ /dev/null @@ -1,878 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS -$$ -BEGIN - uuid_id := substring(entity_id, 8, 8) || '-' || substring(entity_id, 4, 4) || '-1' || substring(entity_id, 1, 3) || - '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12); -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION extract_ts(uuid UUID) RETURNS BIGINT AS -$$ -DECLARE - bytes bytea; -BEGIN - bytes := uuid_send(uuid); - RETURN - ( - ( - (get_byte(bytes, 0)::bigint << 24) | - (get_byte(bytes, 1)::bigint << 16) | - (get_byte(bytes, 2)::bigint << 8) | - (get_byte(bytes, 3)::bigint << 0) - ) + ( - ((get_byte(bytes, 4)::bigint << 8 | - get_byte(bytes, 5)::bigint)) << 32 - ) + ( - (((get_byte(bytes, 6)::bigint & 15) << 8 | get_byte(bytes, 7)::bigint) & 4095) << 48 - ) - 122192928000000000 - ) / 10000::double precision; -END -$$ LANGUAGE plpgsql - IMMUTABLE - PARALLEL SAFE - RETURNS NULL ON NULL INPUT; - - -CREATE OR REPLACE FUNCTION column_type_to_uuid(table_name varchar, column_name varchar) RETURNS VOID - LANGUAGE plpgsql AS -$$ -BEGIN - execute format('ALTER TABLE %s RENAME COLUMN %s TO old_%s;', table_name, column_name, column_name); - execute format('ALTER TABLE %s ADD COLUMN %s UUID;', table_name, column_name); - execute format('UPDATE %s SET %s = to_uuid(old_%s) WHERE old_%s is not null;', table_name, column_name, column_name, column_name); - execute format('ALTER TABLE %s DROP COLUMN old_%s;', table_name, column_name); -END; -$$; - -CREATE OR REPLACE FUNCTION get_column_type(table_name varchar, column_name varchar, OUT data_type varchar) RETURNS varchar - LANGUAGE plpgsql AS -$$ -BEGIN - execute (format('SELECT data_type from information_schema.columns where table_name = %L and column_name = %L', - table_name, column_name)) INTO data_type; -END; -$$; - - -CREATE OR REPLACE PROCEDURE drop_all_idx() - LANGUAGE plpgsql AS -$$ -BEGIN - DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; - DROP INDEX IF EXISTS idx_alarm_originator_created_time; - DROP INDEX IF EXISTS idx_alarm_tenant_created_time; - DROP INDEX IF EXISTS idx_event_type_entity_id; - DROP INDEX IF EXISTS idx_relation_to_id; - DROP INDEX IF EXISTS idx_relation_from_id; - DROP INDEX IF EXISTS idx_device_customer_id; - DROP INDEX IF EXISTS idx_device_customer_id_and_type; - DROP INDEX IF EXISTS idx_device_type; - DROP INDEX IF EXISTS idx_asset_customer_id; - DROP INDEX IF EXISTS idx_asset_customer_id_and_type; - DROP INDEX IF EXISTS idx_asset_type; - DROP INDEX IF EXISTS idx_attribute_kv_by_key_and_last_update_ts; -END; -$$; - -CREATE OR REPLACE PROCEDURE create_all_idx() - LANGUAGE plpgsql AS -$$ -BEGIN - CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC); - CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC); - CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); - CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id); - CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); - CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id); - CREATE INDEX IF NOT EXISTS idx_device_customer_id ON device(tenant_id, customer_id); - CREATE INDEX IF NOT EXISTS idx_device_customer_id_and_type ON device(tenant_id, customer_id, type); - CREATE INDEX IF NOT EXISTS idx_device_type ON device(tenant_id, type); - CREATE INDEX IF NOT EXISTS idx_asset_customer_id ON asset(tenant_id, customer_id); - CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, customer_id, type); - CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); - CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); -END; -$$; - - --- admin_settings -CREATE OR REPLACE PROCEDURE update_admin_settings() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'admin_settings'; - column_id varchar := 'id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE admin_settings DROP CONSTRAINT admin_settings_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE admin_settings ADD CONSTRAINT admin_settings_pkey PRIMARY KEY (id); - ALTER TABLE admin_settings ADD COLUMN created_time BIGINT; - UPDATE admin_settings SET created_time = extract_ts(id) WHERE id is not null; - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; -END; -$$; - - --- alarm -CREATE OR REPLACE PROCEDURE update_alarm() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'alarm'; - column_id varchar := 'id'; - column_originator_id varchar := 'originator_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE alarm DROP CONSTRAINT alarm_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE alarm ADD COLUMN created_time BIGINT; - UPDATE alarm SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE alarm ADD CONSTRAINT alarm_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_originator_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_originator_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_originator_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_originator_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- asset -CREATE OR REPLACE PROCEDURE update_asset() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'asset'; - column_id varchar := 'id'; - column_customer_id varchar := 'customer_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE asset DROP CONSTRAINT asset_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE asset ADD COLUMN created_time BIGINT; - UPDATE asset SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE asset ADD CONSTRAINT asset_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_customer_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_customer_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - ALTER TABLE asset DROP CONSTRAINT asset_name_unq_key; - PERFORM column_type_to_uuid(table_name, column_tenant_id); - ALTER TABLE asset ADD CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; - END; -$$; - --- attribute_kv -CREATE OR REPLACE PROCEDURE update_attribute_kv() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'attribute_kv'; - column_entity_id varchar := 'entity_id'; -BEGIN - data_type := get_column_type(table_name, column_entity_id); - IF data_type = 'character varying' THEN - ALTER TABLE attribute_kv DROP CONSTRAINT attribute_kv_pkey; - PERFORM column_type_to_uuid(table_name, column_entity_id); - ALTER TABLE attribute_kv ADD CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key); - RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id; - END IF; -END; -$$; - --- audit_log -CREATE OR REPLACE PROCEDURE update_audit_log() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'audit_log'; - column_id varchar := 'id'; - column_customer_id varchar := 'customer_id'; - column_tenant_id varchar := 'tenant_id'; - column_entity_id varchar := 'entity_id'; - column_user_id varchar := 'user_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE audit_log DROP CONSTRAINT audit_log_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE audit_log ADD COLUMN created_time BIGINT; - UPDATE audit_log SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE audit_log ADD CONSTRAINT audit_log_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_customer_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_customer_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; - - data_type := get_column_type(table_name, column_entity_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_entity_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id; - END IF; - - data_type := get_column_type(table_name, column_user_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_user_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_user_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_user_id; - END IF; -END; -$$; - - --- component_descriptor -CREATE OR REPLACE PROCEDURE update_component_descriptor() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'component_descriptor'; - column_id varchar := 'id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE component_descriptor DROP CONSTRAINT component_descriptor_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE component_descriptor ADD CONSTRAINT component_descriptor_pkey PRIMARY KEY (id); - ALTER TABLE component_descriptor ADD COLUMN created_time BIGINT; - UPDATE component_descriptor SET created_time = extract_ts(id) WHERE id is not null; - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; -END; -$$; - --- customer -CREATE OR REPLACE PROCEDURE update_customer() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'customer'; - column_id varchar := 'id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE customer DROP CONSTRAINT customer_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE customer ADD CONSTRAINT customer_pkey PRIMARY KEY (id); - ALTER TABLE customer ADD COLUMN created_time BIGINT; - UPDATE customer SET created_time = extract_ts(id) WHERE id is not null; - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- dashboard -CREATE OR REPLACE PROCEDURE update_dashboard() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'dashboard'; - column_id varchar := 'id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE dashboard DROP CONSTRAINT dashboard_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE dashboard ADD CONSTRAINT dashboard_pkey PRIMARY KEY (id); - ALTER TABLE dashboard ADD COLUMN created_time BIGINT; - UPDATE dashboard SET created_time = extract_ts(id) WHERE id is not null; - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - --- device -CREATE OR REPLACE PROCEDURE update_device() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'device'; - column_id varchar := 'id'; - column_customer_id varchar := 'customer_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE device DROP CONSTRAINT device_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE device ADD COLUMN created_time BIGINT; - UPDATE device SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE device ADD CONSTRAINT device_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_customer_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_customer_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - ALTER TABLE device DROP CONSTRAINT device_name_unq_key; - PERFORM column_type_to_uuid(table_name, column_tenant_id); - ALTER TABLE device ADD CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- device_credentials -CREATE OR REPLACE PROCEDURE update_device_credentials() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'device_credentials'; - column_id varchar := 'id'; - column_device_id varchar := 'device_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE device_credentials DROP CONSTRAINT device_credentials_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE device_credentials ADD COLUMN created_time BIGINT; - UPDATE device_credentials SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE device_credentials ADD CONSTRAINT device_credentials_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_device_id); - IF data_type = 'character varying' THEN - ALTER TABLE device_credentials DROP CONSTRAINT IF EXISTS device_credentials_device_id_unq_key; - PERFORM column_type_to_uuid(table_name, column_device_id); - -- remove duplicate credentials with same device_id - DELETE from device_credentials where id in ( - select dc.id - from ( - SELECT id, device_id, - ROW_NUMBER() OVER ( - PARTITION BY - device_id - ORDER BY - created_time DESC - ) row_num - FROM - device_credentials - ) as dc - WHERE dc.row_num > 1 - ); - ALTER TABLE device_credentials ADD CONSTRAINT device_credentials_device_id_unq_key UNIQUE (device_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_device_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_device_id; - END IF; -END; -$$; - - --- event -CREATE OR REPLACE PROCEDURE update_event() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'event'; - column_id varchar := 'id'; - column_entity_id varchar := 'entity_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE event DROP CONSTRAINT event_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE event ADD COLUMN created_time BIGINT; - UPDATE event SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE event ADD CONSTRAINT event_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - ALTER TABLE event DROP CONSTRAINT event_unq_key; - - data_type := get_column_type(table_name, column_entity_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_entity_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; - - ALTER TABLE event ADD CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid); -END; -$$; - - --- relation -CREATE OR REPLACE PROCEDURE update_relation() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'relation'; - column_from_id varchar := 'from_id'; - column_to_id varchar := 'to_id'; -BEGIN - ALTER TABLE relation DROP CONSTRAINT relation_pkey; - - data_type := get_column_type(table_name, column_from_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_from_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_from_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_from_id; - END IF; - - data_type := get_column_type(table_name, column_to_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_to_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_to_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_to_id; - END IF; - - ALTER TABLE relation ADD CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type); -END; -$$; - - --- tb_user -CREATE OR REPLACE PROCEDURE update_tb_user() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'tb_user'; - column_id varchar := 'id'; - column_customer_id varchar := 'customer_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE tb_user DROP CONSTRAINT tb_user_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE tb_user ADD COLUMN created_time BIGINT; - UPDATE tb_user SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE tb_user ADD CONSTRAINT tb_user_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_customer_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_customer_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- tenant -CREATE OR REPLACE PROCEDURE update_tenant() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'tenant'; - column_id varchar := 'id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE tenant DROP CONSTRAINT tenant_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE tenant ADD COLUMN created_time BIGINT; - UPDATE tenant SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE tenant ADD CONSTRAINT tenant_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; -END; -$$; - - --- user_credentials -CREATE OR REPLACE PROCEDURE update_user_credentials() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'user_credentials'; - column_id varchar := 'id'; - column_user_id varchar := 'user_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE user_credentials DROP CONSTRAINT user_credentials_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE user_credentials ADD COLUMN created_time BIGINT; - UPDATE user_credentials SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE user_credentials ADD CONSTRAINT user_credentials_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_user_id); - IF data_type = 'character varying' THEN - ALTER TABLE user_credentials DROP CONSTRAINT user_credentials_user_id_key; - ALTER TABLE user_credentials RENAME COLUMN user_id TO old_user_id; - ALTER TABLE user_credentials ADD COLUMN user_id UUID UNIQUE; - UPDATE user_credentials SET user_id = to_uuid(old_user_id) WHERE old_user_id is not null; - ALTER TABLE user_credentials DROP COLUMN old_user_id; - RAISE NOTICE 'Table % column % updated!', table_name, column_user_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_user_id; - END IF; -END; -$$; - - --- widget_type -CREATE OR REPLACE PROCEDURE update_widget_type() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'widget_type'; - column_id varchar := 'id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE widget_type DROP CONSTRAINT widget_type_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE widget_type ADD COLUMN created_time BIGINT; - UPDATE widget_type SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE widget_type ADD CONSTRAINT widget_type_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- widgets_bundle -CREATE OR REPLACE PROCEDURE update_widgets_bundle() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'widgets_bundle'; - column_id varchar := 'id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE widgets_bundle DROP CONSTRAINT widgets_bundle_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE widgets_bundle ADD COLUMN created_time BIGINT; - UPDATE widgets_bundle SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- rule_chain -CREATE OR REPLACE PROCEDURE update_rule_chain() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'rule_chain'; - column_id varchar := 'id'; - column_first_rule_node_id varchar := 'first_rule_node_id'; - column_tenant_id varchar := 'tenant_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE rule_chain DROP CONSTRAINT rule_chain_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE rule_chain ADD COLUMN created_time BIGINT; - UPDATE rule_chain SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_first_rule_node_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_first_rule_node_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_first_rule_node_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_first_rule_node_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; -END; -$$; - - --- rule_node -CREATE OR REPLACE PROCEDURE update_rule_node() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'rule_node'; - column_id varchar := 'id'; - column_rule_chain_id varchar := 'rule_chain_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE rule_node DROP CONSTRAINT rule_node_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE rule_node ADD COLUMN created_time BIGINT; - UPDATE rule_node SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE rule_node ADD CONSTRAINT rule_node_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_rule_chain_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_rule_chain_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_rule_chain_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_rule_chain_id; - END IF; -END; -$$; - - --- entity_view -CREATE OR REPLACE PROCEDURE update_entity_view() - LANGUAGE plpgsql AS -$$ -DECLARE - data_type varchar; - table_name varchar := 'entity_view'; - column_id varchar := 'id'; - column_entity_id varchar := 'entity_id'; - column_tenant_id varchar := 'tenant_id'; - column_customer_id varchar := 'customer_id'; -BEGIN - data_type := get_column_type(table_name, column_id); - IF data_type = 'character varying' THEN - ALTER TABLE entity_view DROP CONSTRAINT entity_view_pkey; - PERFORM column_type_to_uuid(table_name, column_id); - ALTER TABLE entity_view ADD COLUMN created_time BIGINT; - UPDATE entity_view SET created_time = extract_ts(id) WHERE id is not null; - ALTER TABLE entity_view ADD CONSTRAINT entity_view_pkey PRIMARY KEY (id); - RAISE NOTICE 'Table % column % updated!', table_name, column_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_id; - END IF; - - data_type := get_column_type(table_name, column_entity_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_entity_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_entity_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_entity_id; - END IF; - - data_type := get_column_type(table_name, column_tenant_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_tenant_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_tenant_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_tenant_id; - END IF; - - data_type := get_column_type(table_name, column_customer_id); - IF data_type = 'character varying' THEN - PERFORM column_type_to_uuid(table_name, column_customer_id); - RAISE NOTICE 'Table % column % updated!', table_name, column_customer_id; - ELSE - RAISE NOTICE 'Table % column % already updated!', table_name, column_customer_id; - END IF; -END; -$$; - -CREATE TABLE IF NOT EXISTS ts_kv_latest -( - entity_id uuid NOT NULL, - key int NOT NULL, - ts bigint NOT NULL, - bool_v boolean, - str_v varchar(10000000), - long_v bigint, - dbl_v double precision, - json_v json, - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) -); - -CREATE TABLE IF NOT EXISTS ts_kv_dictionary -( - key varchar(255) NOT NULL, - key_id serial UNIQUE, - CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) -); diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql deleted file mode 100644 index 13a1529eeb..0000000000 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ /dev/null @@ -1,17 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC); diff --git a/application/src/main/data/upgrade/3.1.1/schema_update_after.sql b/application/src/main/data/upgrade/3.1.1/schema_update_after.sql deleted file mode 100644 index 94dec6351d..0000000000 --- a/application/src/main/data/upgrade/3.1.1/schema_update_after.sql +++ /dev/null @@ -1,28 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP PROCEDURE IF EXISTS update_tenant_profiles; -DROP PROCEDURE IF EXISTS update_device_profiles; - -ALTER TABLE tenant ALTER COLUMN tenant_profile_id SET NOT NULL; -ALTER TABLE tenant DROP CONSTRAINT IF EXISTS fk_tenant_profile; -ALTER TABLE tenant ADD CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id); -ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_core; -ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_rule_engine; - -ALTER TABLE device ALTER COLUMN device_profile_id SET NOT NULL; -ALTER TABLE device DROP CONSTRAINT IF EXISTS fk_device_profile; -ALTER TABLE device ADD CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id); diff --git a/application/src/main/data/upgrade/3.1.1/schema_update_before.sql b/application/src/main/data/upgrade/3.1.1/schema_update_before.sql deleted file mode 100644 index 2162341f8a..0000000000 --- a/application/src/main/data/upgrade/3.1.1/schema_update_before.sql +++ /dev/null @@ -1,154 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, - enabled boolean, - created_time bigint NOT NULL, - additional_info varchar, - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - scope varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - login_button_label varchar(255), - login_button_icon varchar(255), - allow_user_creation boolean, - activate_user boolean, - type varchar(31), - basic_email_attribute_key varchar(31), - basic_first_name_attribute_key varchar(31), - basic_last_name_attribute_key varchar(31), - basic_tenant_name_strategy varchar(31), - basic_tenant_name_pattern varchar(255), - basic_customer_name_pattern varchar(255), - basic_default_dashboard_name varchar(255), - basic_always_full_screen boolean, - custom_url varchar(255), - custom_username varchar(255), - custom_password varchar(255), - custom_send_token boolean -); - -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - created_time bigint NOT NULL, - domain_name varchar(255), - domain_scheme varchar(31), - client_registration_info_id uuid -); - -CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - provider_id varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - scope varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - type varchar(31), - basic_email_attribute_key varchar(31), - basic_first_name_attribute_key varchar(31), - basic_last_name_attribute_key varchar(31), - basic_tenant_name_strategy varchar(31), - basic_tenant_name_pattern varchar(255), - basic_customer_name_pattern varchar(255), - basic_default_dashboard_name varchar(255), - basic_always_full_screen boolean, - comment varchar, - login_button_icon varchar(255), - login_button_label varchar(255), - help_link varchar(255), - CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) -); - -CREATE TABLE IF NOT EXISTS device_profile ( - id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - type varchar(255), - transport_type varchar(255), - provision_type varchar(255), - profile_data jsonb, - description varchar, - search_text varchar(255), - is_default boolean, - tenant_id uuid, - default_rule_chain_id uuid, - default_queue_name varchar(255), - provision_device_key varchar, - CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), - CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) -); - -CREATE TABLE IF NOT EXISTS tenant_profile ( - id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - profile_data jsonb, - description varchar, - search_text varchar(255), - is_default boolean, - isolated_tb_core boolean, - isolated_tb_rule_engine boolean, - CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) -); - -CREATE OR REPLACE PROCEDURE update_tenant_profiles() - LANGUAGE plpgsql AS -$$ -BEGIN - UPDATE tenant as t SET tenant_profile_id = p.id - FROM - (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = false) as p - WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = false; - - UPDATE tenant as t SET tenant_profile_id = p.id - FROM - (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = false) as p - WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = false; - - UPDATE tenant as t SET tenant_profile_id = p.id - FROM - (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = true) as p - WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = true; - - UPDATE tenant as t SET tenant_profile_id = p.id - FROM - (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = true) as p - WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = true; -END; -$$; - -CREATE OR REPLACE PROCEDURE update_device_profiles() - LANGUAGE plpgsql AS -$$ -BEGIN - UPDATE device as d SET device_profile_id = p.id, device_data = '{"configuration":{"type":"DEFAULT"}, "transportConfiguration":{"type":"DEFAULT"}}' - FROM - (SELECT id, tenant_id, name from device_profile) as p - WHERE d.device_profile_id IS NULL AND p.tenant_id = d.tenant_id AND d.type = p.name; -END; -$$; diff --git a/application/src/main/data/upgrade/3.2.1/schema_update.sql b/application/src/main/data/upgrade/3.2.1/schema_update.sql deleted file mode 100644 index 14a994b4df..0000000000 --- a/application/src/main/data/upgrade/3.2.1/schema_update.sql +++ /dev/null @@ -1,23 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -ALTER TABLE widget_type - ADD COLUMN IF NOT EXISTS image varchar (1000000), - ADD COLUMN IF NOT EXISTS description varchar (255); - -ALTER TABLE widgets_bundle - ADD COLUMN IF NOT EXISTS image varchar (1000000), - ADD COLUMN IF NOT EXISTS description varchar (255); diff --git a/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql b/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql deleted file mode 100644 index a15a2a3eca..0000000000 --- a/application/src/main/data/upgrade/3.2.1/schema_update_ttl.sql +++ /dev/null @@ -1,87 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid, - IN system_ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE -tenant_cursor CURSOR FOR select tenant.id as tenant_id - from tenant; - tenant_id_record uuid; - customer_id_record uuid; - tenant_ttl bigint; - customer_ttl bigint; - deleted_for_entities bigint; - tenant_ttl_ts bigint; - customer_ttl_ts bigint; -BEGIN -OPEN tenant_cursor; -FETCH tenant_cursor INTO tenant_id_record; -WHILE FOUND - LOOP - EXECUTE format( - 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', - tenant_id_record, 'TTL') INTO tenant_ttl; - if tenant_ttl IS NULL THEN - tenant_ttl := system_ttl; -END IF; - IF tenant_ttl > 0 THEN - tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint; - deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record; - deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record; -END IF; -FOR customer_id_record IN -SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record - LOOP - EXECUTE format( - 'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', - customer_id_record, 'TTL') INTO customer_ttl; -IF customer_ttl IS NULL THEN - customer_ttl_ts := tenant_ttl_ts; -ELSE - IF customer_ttl > 0 THEN - customer_ttl_ts := - (EXTRACT(EPOCH FROM current_timestamp) * 1000 - - customer_ttl::bigint * 1000)::bigint; -END IF; -END IF; - IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN - deleted_for_entities := - delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record; - deleted_for_entities := - delete_device_records_from_ts_kv(tenant_id_record, customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; - deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, - customer_id_record, - customer_ttl_ts); - deleted := deleted + deleted_for_entities; - RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; -END IF; -END LOOP; -FETCH tenant_cursor INTO tenant_id_record; -END LOOP; -END -$$; diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql deleted file mode 100644 index 2caf3b8587..0000000000 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ /dev/null @@ -1,216 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS edge ( - id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - root_rule_chain_id uuid, - type varchar(255), - name varchar(255), - label varchar(255), - routing_key varchar(255), - secret varchar(255), - edge_license_key varchar(30), - cloud_endpoint varchar(255), - search_text varchar(255), - tenant_id uuid, - CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) - ); - -CREATE TABLE IF NOT EXISTS edge_event ( - id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, - created_time bigint NOT NULL, - edge_id uuid, - edge_event_type varchar(255), - edge_event_uid varchar(255), - entity_id uuid, - edge_event_action varchar(255), - body varchar(10000000), - tenant_id uuid, - ts bigint NOT NULL - ); - -CREATE TABLE IF NOT EXISTS resource ( - id uuid NOT NULL CONSTRAINT resource_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid NOT NULL, - title varchar(255) NOT NULL, - resource_type varchar(32) NOT NULL, - resource_key varchar(255) NOT NULL, - search_text varchar(255), - file_name varchar(255) NOT NULL, - data varchar, - CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) -); - -CREATE TABLE IF NOT EXISTS ota_package ( - id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid NOT NULL, - device_profile_id uuid, - type varchar(32) NOT NULL, - title varchar(255) NOT NULL, - version varchar(255) NOT NULL, - tag varchar(255), - url varchar(255), - file_name varchar(255), - content_type varchar(255), - checksum_algorithm varchar(32), - checksum varchar(1020), - data oid, - data_size bigint, - additional_info varchar, - search_text varchar(255), - CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) -); - -CREATE TABLE IF NOT EXISTS oauth2_params ( - id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, - enabled boolean, - tenant_id uuid, - created_time bigint NOT NULL -); - -CREATE TABLE IF NOT EXISTS oauth2_registration ( - id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, - oauth2_params_id uuid NOT NULL, - created_time bigint NOT NULL, - additional_info varchar, - client_id varchar(255), - client_secret varchar(2048), - authorization_uri varchar(255), - token_uri varchar(255), - scope varchar(255), - platforms varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - login_button_label varchar(255), - login_button_icon varchar(255), - allow_user_creation boolean, - activate_user boolean, - type varchar(31), - basic_email_attribute_key varchar(31), - basic_first_name_attribute_key varchar(31), - basic_last_name_attribute_key varchar(31), - basic_tenant_name_strategy varchar(31), - basic_tenant_name_pattern varchar(255), - basic_customer_name_pattern varchar(255), - basic_default_dashboard_name varchar(255), - basic_always_full_screen boolean, - custom_url varchar(255), - custom_username varchar(255), - custom_password varchar(255), - custom_send_token boolean, - CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS oauth2_domain ( - id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, - oauth2_params_id uuid NOT NULL, - created_time bigint NOT NULL, - domain_name varchar(255), - domain_scheme varchar(31), - CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, - CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) -); - -CREATE TABLE IF NOT EXISTS oauth2_mobile ( - id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, - oauth2_params_id uuid NOT NULL, - created_time bigint NOT NULL, - pkg_name varchar(255), - app_secret varchar(2048), - CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, - CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) -); - -ALTER TABLE dashboard - ADD COLUMN IF NOT EXISTS image varchar(1000000), - ADD COLUMN IF NOT EXISTS mobile_hide boolean DEFAULT false, - ADD COLUMN IF NOT EXISTS mobile_order int; - -ALTER TABLE device_profile - ADD COLUMN IF NOT EXISTS image varchar(1000000), - ADD COLUMN IF NOT EXISTS firmware_id uuid, - ADD COLUMN IF NOT EXISTS software_id uuid, - ADD COLUMN IF NOT EXISTS default_dashboard_id uuid; - -ALTER TABLE device - ADD COLUMN IF NOT EXISTS firmware_id uuid, - ADD COLUMN IF NOT EXISTS software_id uuid; - -ALTER TABLE alarm - ADD COLUMN IF NOT EXISTS customer_id uuid; - -DELETE FROM relation WHERE from_type = 'TENANT' AND relation_type_group = 'RULE_CHAIN'; - -DO $$ - BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN - ALTER TABLE device_profile - ADD CONSTRAINT fk_firmware_device_profile - FOREIGN KEY (firmware_id) REFERENCES ota_package(id); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN - ALTER TABLE device_profile - ADD CONSTRAINT fk_software_device_profile - FOREIGN KEY (firmware_id) REFERENCES ota_package(id); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN - ALTER TABLE device_profile - ADD CONSTRAINT fk_default_dashboard_device_profile - FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN - ALTER TABLE device - ADD CONSTRAINT fk_firmware_device - FOREIGN KEY (firmware_id) REFERENCES ota_package(id); - END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN - ALTER TABLE device - ADD CONSTRAINT fk_software_device - FOREIGN KEY (firmware_id) REFERENCES ota_package(id); - END IF; - END; -$$; - - -ALTER TABLE api_usage_state - ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32); -UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL; - -CREATE TABLE IF NOT EXISTS rpc ( - id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid NOT NULL, - device_id uuid NOT NULL, - expiration_time bigint NOT NULL, - request varchar(10000000) NOT NULL, - response varchar(10000000), - additional_info varchar(10000000), - status varchar(255) NOT NULL -); - -CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id); diff --git a/application/src/main/data/upgrade/3.2.2/schema_update_event.sql b/application/src/main/data/upgrade/3.2.2/schema_update_event.sql deleted file mode 100644 index e89312f783..0000000000 --- a/application/src/main/data/upgrade/3.2.2/schema_update_event.sql +++ /dev/null @@ -1,90 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - --- PROCEDURE: public.cleanup_events_by_ttl(bigint, bigint, bigint) - -DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint); - -CREATE OR REPLACE PROCEDURE public.cleanup_events_by_ttl( - ttl bigint, - debug_ttl bigint, - INOUT deleted bigint) -LANGUAGE 'plpgsql' -AS $BODY$ -DECLARE - ttl_ts bigint; - debug_ttl_ts bigint; - ttl_deleted_count bigint DEFAULT 0; - debug_ttl_deleted_count bigint DEFAULT 0; -BEGIN - IF ttl > 0 THEN - ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; - - DELETE FROM event - WHERE ts < ttl_ts - AND NOT event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION'); - - GET DIAGNOSTICS ttl_deleted_count = ROW_COUNT; - END IF; - - IF debug_ttl > 0 THEN - debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint; - - DELETE FROM event - WHERE ts < debug_ttl_ts - AND event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION'); - - GET DIAGNOSTICS debug_ttl_deleted_count = ROW_COUNT; - END IF; - - RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count; - RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count; - deleted := ttl_deleted_count + debug_ttl_deleted_count; -END -$BODY$; - - --- Index: idx_event_ts - -DROP INDEX IF EXISTS public.idx_event_ts; - --- Hint: add CONCURRENTLY to CREATE INDEX query in case of more then 1 million records or during live update --- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_ts -CREATE INDEX IF NOT EXISTS idx_event_ts - ON public.event - (ts DESC NULLS LAST) - WITH (FILLFACTOR=95); - -COMMENT ON INDEX public.idx_event_ts - IS 'This index helps to delete events by TTL using timestamp'; - - --- Index: idx_event_tenant_entity_type_entity_event_type_created_time_des - -DROP INDEX IF EXISTS public.idx_event_tenant_entity_type_entity_event_type_created_time_des; - --- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des -CREATE INDEX IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des - ON public.event - (tenant_id ASC, entity_type ASC, entity_id ASC, event_type ASC, created_time DESC NULLS LAST) - WITH (FILLFACTOR=95); - -COMMENT ON INDEX public.idx_event_tenant_entity_type_entity_event_type_created_time_des - IS 'This index helps to open latest events on UI fast'; - --- Index: idx_event_type_entity_id --- Description: replaced with more suitable idx_event_tenant_entity_type_entity_event_type_created_time_des -DROP INDEX IF EXISTS public.idx_event_type_entity_id; \ No newline at end of file diff --git a/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql b/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql deleted file mode 100644 index ed0cb70ed1..0000000000 --- a/application/src/main/data/upgrade/3.2.2/schema_update_ttl.sql +++ /dev/null @@ -1,32 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - ttl_ts bigint; - ttl_deleted_count bigint DEFAULT 0; -BEGIN - IF ttl > 0 THEN - ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; - EXECUTE format( - 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count; - END IF; - RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count; - deleted := ttl_deleted_count; -END -$$; diff --git a/application/src/main/data/upgrade/3.3.2/schema_update.sql b/application/src/main/data/upgrade/3.3.2/schema_update.sql deleted file mode 100644 index 00ab32d761..0000000000 --- a/application/src/main/data/upgrade/3.3.2/schema_update.sql +++ /dev/null @@ -1,71 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS entity_alarm ( - tenant_id uuid NOT NULL, - entity_type varchar(32), - entity_id uuid NOT NULL, - created_time bigint NOT NULL, - alarm_type varchar(255) NOT NULL, - customer_id uuid, - alarm_id uuid, - CONSTRAINT entity_alarm_pkey PRIMARY KEY (entity_id, alarm_id), - CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE -); - -CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_entity_alarm_alarm_id ON entity_alarm(alarm_id); - -INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id) -SELECT tenant_id, - CASE - WHEN originator_type = 0 THEN 'TENANT' - WHEN originator_type = 1 THEN 'CUSTOMER' - WHEN originator_type = 2 THEN 'USER' - WHEN originator_type = 3 THEN 'DASHBOARD' - WHEN originator_type = 4 THEN 'ASSET' - WHEN originator_type = 5 THEN 'DEVICE' - WHEN originator_type = 6 THEN 'ALARM' - WHEN originator_type = 7 THEN 'RULE_CHAIN' - WHEN originator_type = 8 THEN 'RULE_NODE' - WHEN originator_type = 9 THEN 'ENTITY_VIEW' - WHEN originator_type = 10 THEN 'WIDGETS_BUNDLE' - WHEN originator_type = 11 THEN 'WIDGET_TYPE' - WHEN originator_type = 12 THEN 'TENANT_PROFILE' - WHEN originator_type = 13 THEN 'DEVICE_PROFILE' - WHEN originator_type = 14 THEN 'API_USAGE_STATE' - WHEN originator_type = 15 THEN 'TB_RESOURCE' - WHEN originator_type = 16 THEN 'OTA_PACKAGE' - WHEN originator_type = 17 THEN 'EDGE' - WHEN originator_type = 18 THEN 'RPC' - else 'UNKNOWN' - END, - originator_id, - created_time, - type, - customer_id, - id -FROM alarm -ON CONFLICT DO NOTHING; - -INSERT INTO entity_alarm(tenant_id, entity_type, entity_id, created_time, alarm_type, customer_id, alarm_id) -SELECT a.tenant_id, r.from_type, r.from_id, created_time, type, customer_id, id -FROM alarm a - INNER JOIN relation r ON r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id -ON CONFLICT DO NOTHING; - -DELETE FROM relation r WHERE r.relation_type_group = 'ALARM'; \ No newline at end of file diff --git a/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql b/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql deleted file mode 100644 index 9066f5c34d..0000000000 --- a/application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql +++ /dev/null @@ -1,213 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - - -CREATE OR REPLACE PROCEDURE update_profile_bootstrap() - LANGUAGE plpgsql AS -$$ - -BEGIN - - UPDATE device_profile - SET profile_data = jsonb_set( - profile_data, - '{transportConfiguration}', - get_bootstrap( - profile_data::jsonb #> '{transportConfiguration}', - subquery.publickey_bs, - subquery.publickey_lw, - profile_data::json #>> '{transportConfiguration, bootstrap, bootstrapServer, securityMode}', - profile_data::json #>> '{transportConfiguration, bootstrap, lwm2mServer, securityMode}'), - true) - FROM ( - SELECT id, - encode( - decode(profile_data::json #> '{transportConfiguration,bootstrap,bootstrapServer}' ->> - 'serverPublicKey', 'hex')::bytea, 'base64') AS publickey_bs, - encode( - decode(profile_data::json #> '{transportConfiguration,bootstrap,lwm2mServer}' ->> - 'serverPublicKey', 'hex')::bytea, 'base64') AS publickey_lw - FROM device_profile - WHERE transport_type = 'LWM2M' - ) AS subquery - WHERE device_profile.id = subquery.id - AND subquery.publickey_bs IS NOT NULL - AND subquery.publickey_lw IS NOT NULL; - -END; -$$; - -CREATE OR REPLACE FUNCTION get_bootstrap(transport_configuration_in jsonb, publickey_bs text, - publickey_lw text, security_mode_bs text, - security_mode_lw text) RETURNS jsonb AS -$$ - -DECLARE - bootstrap_new jsonb; - bootstrap_in jsonb; - -BEGIN - - IF security_mode_lw IS NULL THEN - security_mode_lw := 'NO_SEC'; - END IF; - - IF security_mode_bs IS NULL THEN - security_mode_bs := 'NO_SEC'; - END IF; - - bootstrap_in := transport_configuration_in::jsonb #> '{bootstrap}'; - bootstrap_new := json_build_array( - json_build_object('shortServerId', bootstrap_in::json #> '{bootstrapServer}' -> 'serverId', - 'securityMode', security_mode_bs, - 'binding', bootstrap_in::json #> '{servers}' ->> 'binding', - 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime', - 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled', - 'defaultMinPeriod', bootstrap_in::json #> '{servers}' -> 'defaultMinPeriod', - 'host', bootstrap_in::json #> '{bootstrapServer}' ->> 'host', - 'port', bootstrap_in::json #> '{bootstrapServer}' -> 'port', - 'serverPublicKey', publickey_bs, - 'bootstrapServerIs', true, - 'clientHoldOffTime', bootstrap_in::json #> '{bootstrapServer}' -> 'clientHoldOffTime', - 'bootstrapServerAccountTimeout', - bootstrap_in::json #> '{bootstrapServer}' -> 'bootstrapServerAccountTimeout' - ), - json_build_object('shortServerId', bootstrap_in::json #> '{lwm2mServer}' -> 'serverId', - 'securityMode', security_mode_lw, - 'binding', bootstrap_in::json #> '{servers}' ->> 'binding', - 'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime', - 'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled', - 'defaultMinPeriod', bootstrap_in::json #> '{servers}' -> 'defaultMinPeriod', - 'host', bootstrap_in::json #> '{lwm2mServer}' ->> 'host', - 'port', bootstrap_in::json #> '{lwm2mServer}' -> 'port', - 'serverPublicKey', publickey_lw, - 'bootstrapServerIs', false, - 'clientHoldOffTime', bootstrap_in::json #> '{lwm2mServer}' -> 'clientHoldOffTime', - 'bootstrapServerAccountTimeout', - bootstrap_in::json #> '{lwm2mServer}' -> 'bootstrapServerAccountTimeout' - ) - ); - RETURN jsonb_set( - transport_configuration_in, - '{bootstrap}', - bootstrap_new, - true) || '{"bootstrapServerUpdateEnable": true}'; - -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE PROCEDURE update_device_credentials_to_base64_and_bootstrap() - LANGUAGE plpgsql AS -$$ - -BEGIN - - UPDATE device_credentials - SET credentials_value = get_device_and_bootstrap(credentials_value::text) - WHERE credentials_type = 'LWM2M_CREDENTIALS'; -END; -$$; - -CREATE OR REPLACE FUNCTION get_device_and_bootstrap(IN credentials_value text, OUT credentials_value_new text) - LANGUAGE plpgsql AS -$$ -DECLARE - client_secret_key text; - client_public_key_or_id text; - client_key_value_object jsonb; - client_bootstrap_server_value_object jsonb; - client_bootstrap_server_object jsonb; - client_bootstrap_object jsonb; - -BEGIN - credentials_value_new := credentials_value; - IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'RPK' AND - NULLIF((credentials_value::jsonb #> '{client}' ->> 'key' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN - client_public_key_or_id := encode(decode(credentials_value::jsonb #> '{client}' ->> 'key', 'hex')::bytea, 'base64'); - client_key_value_object := json_build_object( - 'endpoint', credentials_value::jsonb #> '{client}' ->> 'endpoint', - 'securityConfigClientMode', credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode', - 'key', client_public_key_or_id); - credentials_value_new := - credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb; - END IF; - IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'X509' AND - NULLIF((credentials_value::jsonb #> '{client}' ->> 'cert' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN - client_public_key_or_id := - encode(decode(credentials_value::jsonb #> '{client}' ->> 'cert', 'hex')::bytea, 'base64'); - client_key_value_object := json_build_object( - 'endpoint', credentials_value::jsonb #> '{client}' ->> 'endpoint', - 'securityConfigClientMode', credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode', - 'cert', client_public_key_or_id); - credentials_value_new := - credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb; - END IF; - - IF credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'RPK' OR - credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'X509' THEN - IF NULLIF((credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text, - 'false') = 'true' AND - NULLIF( - (credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientPublicKeyOrId' ~ '^[0-9a-fA-F]+$')::text, - 'false') = 'true' THEN - client_secret_key := - encode(decode(credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey', 'hex')::bytea, - 'base64'); - client_public_key_or_id := encode( - decode(credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientPublicKeyOrId', 'hex')::bytea, - 'base64'); - client_bootstrap_server_value_object := jsonb_build_object( - 'securityMode', credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode', - 'clientPublicKeyOrId', client_public_key_or_id, - 'clientSecretKey', client_secret_key - ); - client_bootstrap_server_object := jsonb_build_object('lwm2mServer', client_bootstrap_server_value_object::jsonb); - client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb; - credentials_value_new := - jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb; - END IF; - END IF; - - IF credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'RPK' OR - credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'X509' THEN - IF NULLIF( - (credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text, - 'false') = 'true' AND - NULLIF( - (credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientPublicKeyOrId' ~ '^[0-9a-fA-F]+$')::text, - 'false') = 'true' THEN - client_secret_key := - encode( - decode(credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientSecretKey', 'hex')::bytea, - 'base64'); - client_public_key_or_id := encode( - decode(credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'clientPublicKeyOrId', 'hex')::bytea, - 'base64'); - client_bootstrap_server_value_object := jsonb_build_object( - 'securityMode', credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode', - 'clientPublicKeyOrId', client_public_key_or_id, - 'clientSecretKey', client_secret_key - ); - client_bootstrap_server_object := - jsonb_build_object('bootstrapServer', client_bootstrap_server_value_object::jsonb); - client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb; - credentials_value_new := - jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb; - END IF; - END IF; - -END; -$$; \ No newline at end of file diff --git a/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql b/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql deleted file mode 100644 index 10a5384040..0000000000 --- a/application/src/main/data/upgrade/3.3.3/schema_event_ttl_procedure.sql +++ /dev/null @@ -1,50 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - - -DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint); - -CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl( - IN regular_events_start_ts bigint, - IN regular_events_end_ts bigint, - IN debug_events_start_ts bigint, - IN debug_events_end_ts bigint, - INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - ttl_deleted_count bigint DEFAULT 0; - debug_ttl_deleted_count bigint DEFAULT 0; -BEGIN - IF regular_events_start_ts > 0 AND regular_events_end_ts > 0 THEN - EXECUTE format( - 'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' || - '(event_type != %L::varchar AND event_type != %L::varchar)) RETURNING *) ' || - 'SELECT count(*) FROM deleted', regular_events_start_ts, regular_events_end_ts, - 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count; - END IF; - IF debug_events_start_ts > 0 AND debug_events_end_ts > 0 THEN - EXECUTE format( - 'WITH deleted AS (DELETE FROM event WHERE id in (SELECT id from event WHERE ts > %L::bigint AND ts < %L::bigint AND ' || - '(event_type = %L::varchar OR event_type = %L::varchar)) RETURNING *) ' || - 'SELECT count(*) FROM deleted', debug_events_start_ts, debug_events_end_ts, - 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count; - END IF; - RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count; - RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count; - deleted := ttl_deleted_count + debug_ttl_deleted_count; -END -$$; diff --git a/application/src/main/data/upgrade/3.3.3/schema_update.sql b/application/src/main/data/upgrade/3.3.3/schema_update.sql deleted file mode 100644 index b9a43435da..0000000000 --- a/application/src/main/data/upgrade/3.3.3/schema_update.sql +++ /dev/null @@ -1,29 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DELETE from ota_package as op WHERE NOT EXISTS(SELECT * FROM device_profile dp where op.device_profile_id = dp.id); - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_device_profile_ota_package') THEN - ALTER TABLE ota_package - ADD CONSTRAINT fk_device_profile_ota_package - FOREIGN KEY (device_profile_id) REFERENCES device_profile (id) - ON DELETE CASCADE; - END IF; - END; -$$; diff --git a/application/src/main/data/upgrade/3.3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql deleted file mode 100644 index dff72ca33b..0000000000 --- a/application/src/main/data/upgrade/3.3.4/schema_update.sql +++ /dev/null @@ -1,140 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -ALTER TABLE device - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE device_profile - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE asset - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE rule_chain - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE rule_node - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE dashboard - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE customer - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE widgets_bundle - ADD COLUMN IF NOT EXISTS external_id UUID; -ALTER TABLE entity_view - ADD COLUMN IF NOT EXISTS external_id UUID; - -CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, external_id); -CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); - -ALTER TABLE admin_settings - ADD COLUMN IF NOT EXISTS tenant_id uuid NOT NULL DEFAULT '13814000-1dd2-11b2-8080-808080808080'; - -CREATE TABLE IF NOT EXISTS queue ( - id uuid NOT NULL CONSTRAINT queue_pkey PRIMARY KEY, - created_time bigint NOT NULL, - tenant_id uuid, - name varchar(255), - topic varchar(255), - poll_interval int, - partitions int, - consumer_per_partition boolean, - pack_processing_timeout bigint, - submit_strategy varchar(255), - processing_strategy varchar(255), - additional_info varchar -); - -CREATE TABLE IF NOT EXISTS user_auth_settings ( - id uuid NOT NULL CONSTRAINT user_auth_settings_pkey PRIMARY KEY, - created_time bigint NOT NULL, - user_id uuid UNIQUE NOT NULL CONSTRAINT fk_user_auth_settings_user_id REFERENCES tb_user(id), - two_fa_settings varchar -); - -CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id); - -ALTER TABLE tenant_profile DROP COLUMN IF EXISTS isolated_tb_core; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_external_id_unq_key') THEN - ALTER TABLE device ADD CONSTRAINT device_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'device_profile_external_id_unq_key') THEN - ALTER TABLE device_profile ADD CONSTRAINT device_profile_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'asset_external_id_unq_key') THEN - ALTER TABLE asset ADD CONSTRAINT asset_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'rule_chain_external_id_unq_key') THEN - ALTER TABLE rule_chain ADD CONSTRAINT rule_chain_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'dashboard_external_id_unq_key') THEN - ALTER TABLE dashboard ADD CONSTRAINT dashboard_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'customer_external_id_unq_key') THEN - ALTER TABLE customer ADD CONSTRAINT customer_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widgets_bundle_external_id_unq_key') THEN - ALTER TABLE widgets_bundle ADD CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - -DO -$$ - BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'entity_view_external_id_unq_key') THEN - ALTER TABLE entity_view ADD CONSTRAINT entity_view_external_id_unq_key UNIQUE (tenant_id, external_id); - END IF; - END; -$$; - diff --git a/application/src/main/data/upgrade/3.4.0/schema_update.sql b/application/src/main/data/upgrade/3.4.0/schema_update.sql deleted file mode 100644 index c511210e1b..0000000000 --- a/application/src/main/data/upgrade/3.4.0/schema_update.sql +++ /dev/null @@ -1,234 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS rule_node_debug_event ( - id uuid NOT NULL, - tenant_id uuid NOT NULL , - ts bigint NOT NULL, - entity_id uuid NOT NULL, - service_id varchar, - e_type varchar, - e_entity_id uuid, - e_entity_type varchar, - e_msg_id uuid, - e_msg_type varchar, - e_data_type varchar, - e_relation_type varchar, - e_data varchar, - e_metadata varchar, - e_error varchar -) PARTITION BY RANGE (ts); - -CREATE TABLE IF NOT EXISTS rule_chain_debug_event ( - id uuid NOT NULL, - tenant_id uuid NOT NULL, - ts bigint NOT NULL, - entity_id uuid NOT NULL, - service_id varchar NOT NULL, - e_message varchar, - e_error varchar -) PARTITION BY RANGE (ts); - -CREATE TABLE IF NOT EXISTS stats_event ( - id uuid NOT NULL, - tenant_id uuid NOT NULL, - ts bigint NOT NULL, - entity_id uuid NOT NULL, - service_id varchar NOT NULL, - e_messages_processed bigint NOT NULL, - e_errors_occurred bigint NOT NULL -) PARTITION BY RANGE (ts); - -CREATE TABLE IF NOT EXISTS lc_event ( - id uuid NOT NULL, - tenant_id uuid NOT NULL, - ts bigint NOT NULL, - entity_id uuid NOT NULL, - service_id varchar NOT NULL, - e_type varchar NOT NULL, - e_success boolean NOT NULL, - e_error varchar -) PARTITION BY RANGE (ts); - -CREATE TABLE IF NOT EXISTS error_event ( - id uuid NOT NULL, - tenant_id uuid NOT NULL, - ts bigint NOT NULL, - entity_id uuid NOT NULL, - service_id varchar NOT NULL, - e_method varchar NOT NULL, - e_error varchar -) PARTITION BY RANGE (ts); - -CREATE INDEX IF NOT EXISTS idx_rule_node_debug_event_main - ON rule_node_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95); - -CREATE INDEX IF NOT EXISTS idx_rule_chain_debug_event_main - ON rule_chain_debug_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95); - -CREATE INDEX IF NOT EXISTS idx_stats_event_main - ON stats_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95); - -CREATE INDEX IF NOT EXISTS idx_lc_event_main - ON lc_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95); - -CREATE INDEX IF NOT EXISTS idx_error_event_main - ON error_event (tenant_id ASC, entity_id ASC, ts DESC NULLS LAST) WITH (FILLFACTOR=95); - -CREATE OR REPLACE FUNCTION to_safe_json(p_json text) RETURNS json -LANGUAGE plpgsql AS -$$ -BEGIN - return REPLACE(p_json, '\u0000', '' )::json; -EXCEPTION - WHEN OTHERS THEN - return '{}'::json; -END; -$$; - --- Useful to migrate old events to the new table structure; -CREATE OR REPLACE PROCEDURE migrate_regular_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int) - LANGUAGE plpgsql AS -$$ -DECLARE - partition_size_in_ms bigint; - p record; - table_name varchar; -BEGIN - partition_size_in_ms = partition_size_in_hours * 3600 * 1000; - - FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('STATS', 'LC_EVENT', 'ERROR') and ts >= start_ts_in_ms and ts < end_ts_in_ms - LOOP - IF p.event_type = 'STATS' THEN - table_name := 'stats_event'; - ELSEIF p.event_type = 'LC_EVENT' THEN - table_name := 'lc_event'; - ELSEIF p.event_type = 'ERROR' THEN - table_name := 'error_event'; - END IF; - RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms); - EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms)); - END LOOP; - - INSERT INTO stats_event - SELECT id, - tenant_id, - ts, - entity_id, - body ->> 'server', - (body ->> 'messagesProcessed')::bigint, - (body ->> 'errorsOccurred')::bigint - FROM - (select id, tenant_id, ts, entity_id, to_safe_json(body) as body - FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'STATS' AND to_safe_json(body) ->> 'server' IS NOT NULL - ) safe_event - ON CONFLICT DO NOTHING; - - INSERT INTO lc_event - SELECT id, - tenant_id, - ts, - entity_id, - body ->> 'server', - body ->> 'event', - (body ->> 'success')::boolean, - body ->> 'error' - FROM - (select id, tenant_id, ts, entity_id, to_safe_json(body) as body - FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'LC_EVENT' AND to_safe_json(body) ->> 'server' IS NOT NULL - ) safe_event - ON CONFLICT DO NOTHING; - - INSERT INTO error_event - SELECT id, - tenant_id, - ts, - entity_id, - body ->> 'server', - body ->> 'method', - body ->> 'error' - FROM - (select id, tenant_id, ts, entity_id, to_safe_json(body) as body - FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'ERROR' AND to_safe_json(body) ->> 'server' IS NOT NULL - ) safe_event - ON CONFLICT DO NOTHING; - -END -$$; - --- Useful to migrate old debug events to the new table structure; -CREATE OR REPLACE PROCEDURE migrate_debug_events(IN start_ts_in_ms bigint, IN end_ts_in_ms bigint, IN partition_size_in_hours int) - LANGUAGE plpgsql AS -$$ -DECLARE - partition_size_in_ms bigint; - p record; - table_name varchar; -BEGIN - partition_size_in_ms = partition_size_in_hours * 3600 * 1000; - - FOR p IN SELECT DISTINCT event_type as event_type, (created_time - created_time % partition_size_in_ms) as partition_ts FROM event e WHERE e.event_type in ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') and ts >= start_ts_in_ms and ts < end_ts_in_ms - LOOP - IF p.event_type = 'DEBUG_RULE_NODE' THEN - table_name := 'rule_node_debug_event'; - ELSEIF p.event_type = 'DEBUG_RULE_CHAIN' THEN - table_name := 'rule_chain_debug_event'; - END IF; - RAISE NOTICE '[%] Partition to create : [%-%]', table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms); - EXECUTE format('CREATE TABLE IF NOT EXISTS %s_%s PARTITION OF %s FOR VALUES FROM ( %s ) TO ( %s )', table_name, p.partition_ts, table_name, p.partition_ts, (p.partition_ts + partition_size_in_ms)); - END LOOP; - - INSERT INTO rule_node_debug_event - SELECT id, - tenant_id, - ts, - entity_id, - body ->> 'server', - body ->> 'type', - (body ->> 'entityId')::uuid, - body ->> 'entityName', - (body ->> 'msgId')::uuid, - body ->> 'msgType', - body ->> 'dataType', - body ->> 'relationType', - body ->> 'data', - body ->> 'metadata', - body ->> 'error' - FROM - (select id, tenant_id, ts, entity_id, to_safe_json(body) as body - FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_NODE' AND to_safe_json(body) ->> 'server' IS NOT NULL - ) safe_event - ON CONFLICT DO NOTHING; - - INSERT INTO rule_chain_debug_event - SELECT id, - tenant_id, - ts, - entity_id, - body ->> 'server', - body ->> 'message', - body ->> 'error' - FROM - (select id, tenant_id, ts, entity_id, to_safe_json(body) as body - FROM event WHERE ts >= start_ts_in_ms and ts < end_ts_in_ms AND event_type = 'DEBUG_RULE_CHAIN' AND to_safe_json(body) ->> 'server' IS NOT NULL - ) safe_event - ON CONFLICT DO NOTHING; -END -$$; - -UPDATE tb_user - SET additional_info = REPLACE(additional_info, '"lang":"ja_JA"', '"lang":"ja_JP"') - WHERE additional_info LIKE '%"lang":"ja_JA"%'; diff --git a/application/src/main/data/upgrade/3.4.1/schema_update.sql b/application/src/main/data/upgrade/3.4.1/schema_update.sql deleted file mode 100644 index c0bcdaca39..0000000000 --- a/application/src/main/data/upgrade/3.4.1/schema_update.sql +++ /dev/null @@ -1,142 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - --- AUDIT LOGS MIGRATION START -DO -$$ - DECLARE table_partition RECORD; - BEGIN - -- in case of running the upgrade script a second time: - IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_audit_log')) THEN - ALTER TABLE audit_log RENAME TO old_audit_log; - CREATE INDEX IF NOT EXISTS idx_old_audit_log_created_time ON old_audit_log(created_time); - - ALTER INDEX IF EXISTS idx_audit_log_tenant_id_and_created_time RENAME TO idx_old_audit_log_tenant_id_and_created_time; - - FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts - FROM pg_tables WHERE tablename LIKE 'audit_log_%' - LOOP - EXECUTE format('ALTER TABLE %s RENAME TO old_audit_log_%s', table_partition.name, table_partition.partition_ts); - END LOOP; - ELSE - RAISE NOTICE 'Table old_audit_log already exists, leaving as is'; - END IF; - END; -$$; - -CREATE TABLE IF NOT EXISTS audit_log ( - id uuid NOT NULL, - created_time bigint NOT NULL, - tenant_id uuid, - customer_id uuid, - entity_id uuid, - entity_type varchar(255), - entity_name varchar(255), - user_id uuid, - user_name varchar(255), - action_type varchar(255), - action_data varchar(1000000), - action_status varchar(255), - action_failure_details varchar(1000000) -) PARTITION BY RANGE (created_time); -CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_audit_log_id ON audit_log(id); - -CREATE OR REPLACE PROCEDURE migrate_audit_logs(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT) - LANGUAGE plpgsql AS -$$ -DECLARE - p RECORD; - partition_end_ts BIGINT; -BEGIN - FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_audit_log - WHERE created_time >= start_time_ms AND created_time < end_time_ms - LOOP - partition_end_ts = p.partition_ts + partition_size_ms; - RAISE NOTICE '[audit_log] Partition to create : [%-%]', p.partition_ts, partition_end_ts; - EXECUTE format('CREATE TABLE IF NOT EXISTS audit_log_%s PARTITION OF audit_log ' || - 'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts); - END LOOP; - - INSERT INTO audit_log - SELECT id, created_time, tenant_id, customer_id, entity_id, entity_type, entity_name, user_id, user_name, action_type, action_data, action_status, action_failure_details - FROM old_audit_log - WHERE created_time >= start_time_ms AND created_time < end_time_ms; -END; -$$; --- AUDIT LOGS MIGRATION END - - --- EDGE EVENTS MIGRATION START -DO -$$ - DECLARE table_partition RECORD; - BEGIN - -- in case of running the upgrade script a second time: - IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN - ALTER TABLE edge_event RENAME TO old_edge_event; - CREATE INDEX IF NOT EXISTS idx_old_edge_event_created_time_tmp ON old_edge_event(created_time); - ALTER INDEX IF EXISTS idx_edge_event_tenant_id_and_created_time RENAME TO idx_old_edge_event_tenant_id_and_created_time; - - FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts - FROM pg_tables WHERE tablename LIKE 'edge_event_%' - LOOP - EXECUTE format('ALTER TABLE %s RENAME TO old_edge_event_%s', table_partition.name, table_partition.partition_ts); - END LOOP; - ELSE - RAISE NOTICE 'Table old_edge_event already exists, leaving as is'; - END IF; -END; -$$; - -CREATE TABLE IF NOT EXISTS edge_event ( - id uuid NOT NULL, - created_time bigint NOT NULL, - edge_id uuid, - edge_event_type varchar(255), - edge_event_uid varchar(255), - entity_id uuid, - edge_event_action varchar(255), - body varchar(10000000), - tenant_id uuid, - ts bigint NOT NULL - ) PARTITION BY RANGE (created_time); -CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC); -CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id); - -CREATE OR REPLACE PROCEDURE migrate_edge_event(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT) - LANGUAGE plpgsql AS -$$ -DECLARE - p RECORD; - partition_end_ts BIGINT; -BEGIN - FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_edge_event - WHERE created_time >= start_time_ms AND created_time < end_time_ms - LOOP - partition_end_ts = p.partition_ts + partition_size_ms; - RAISE NOTICE '[edge_event] Partition to create : [%-%]', p.partition_ts, partition_end_ts; - EXECUTE format('CREATE TABLE IF NOT EXISTS edge_event_%s PARTITION OF edge_event ' || - 'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts); - END LOOP; - - INSERT INTO edge_event - SELECT id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts - FROM old_edge_event - WHERE created_time >= start_time_ms AND created_time < end_time_ms; -END; -$$; --- EDGE EVENTS MIGRATION END diff --git a/application/src/main/data/upgrade/3.4.1/schema_update_after.sql b/application/src/main/data/upgrade/3.4.1/schema_update_after.sql deleted file mode 100644 index 6d1ee4d8a8..0000000000 --- a/application/src/main/data/upgrade/3.4.1/schema_update_after.sql +++ /dev/null @@ -1,21 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -DROP PROCEDURE IF EXISTS update_asset_profiles; - -ALTER TABLE asset ALTER COLUMN asset_profile_id SET NOT NULL; -ALTER TABLE asset DROP CONSTRAINT IF EXISTS fk_asset_profile; -ALTER TABLE asset ADD CONSTRAINT fk_asset_profile FOREIGN KEY (asset_profile_id) REFERENCES asset_profile(id); diff --git a/application/src/main/data/upgrade/3.4.1/schema_update_before.sql b/application/src/main/data/upgrade/3.4.1/schema_update_before.sql deleted file mode 100644 index f2c849fb24..0000000000 --- a/application/src/main/data/upgrade/3.4.1/schema_update_before.sql +++ /dev/null @@ -1,46 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - -CREATE TABLE IF NOT EXISTS asset_profile ( - id uuid NOT NULL CONSTRAINT asset_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - image varchar(1000000), - description varchar, - search_text varchar(255), - is_default boolean, - tenant_id uuid, - default_rule_chain_id uuid, - default_dashboard_id uuid, - default_queue_name varchar(255), - external_id uuid, - CONSTRAINT asset_profile_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT asset_profile_external_id_unq_key UNIQUE (tenant_id, external_id), - CONSTRAINT fk_default_rule_chain_asset_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), - CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id) - ); - -CREATE OR REPLACE PROCEDURE update_asset_profiles() - LANGUAGE plpgsql AS -$$ -BEGIN - UPDATE asset a SET asset_profile_id = COALESCE( - (SELECT id from asset_profile p WHERE p.tenant_id = a.tenant_id AND a.type = p.name), - (SELECT id from asset_profile p WHERE p.tenant_id = a.tenant_id AND p.name = 'default') - ) - WHERE a.asset_profile_id IS NULL; -END; -$$; diff --git a/application/src/main/data/upgrade/3.4.4/schema_update.sql b/application/src/main/data/upgrade/3.4.4/schema_update.sql deleted file mode 100644 index 37b1b3a7e0..0000000000 --- a/application/src/main/data/upgrade/3.4.4/schema_update.sql +++ /dev/null @@ -1,379 +0,0 @@ --- --- Copyright © 2016-2024 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. --- - --- USER CREDENTIALS START - -ALTER TABLE user_credentials - ADD COLUMN IF NOT EXISTS additional_info varchar NOT NULL DEFAULT '{}'; - -UPDATE user_credentials - SET additional_info = json_build_object('userPasswordHistory', (u.additional_info::json -> 'userPasswordHistory')) - FROM tb_user u WHERE user_credentials.user_id = u.id AND u.additional_info::jsonb ? 'userPasswordHistory'; - -UPDATE tb_user SET additional_info = tb_user.additional_info::jsonb - 'userPasswordHistory' WHERE additional_info::jsonb ? 'userPasswordHistory'; - --- USER CREDENTIALS END - --- ALARM ASSIGN TO USER START - -ALTER TABLE alarm ADD COLUMN IF NOT EXISTS assign_ts BIGINT DEFAULT 0; -ALTER TABLE alarm ADD COLUMN IF NOT EXISTS assignee_id UUID; - -CREATE INDEX IF NOT EXISTS idx_alarm_tenant_assignee_created_time ON alarm(tenant_id, assignee_id, created_time DESC); - --- ALARM ASSIGN TO USER END - --- ALARM STATUS REFACTORING START - -ALTER TABLE alarm ADD COLUMN IF NOT EXISTS acknowledged boolean; -ALTER TABLE alarm ADD COLUMN IF NOT EXISTS cleared boolean; - -ALTER TABLE alarm ADD COLUMN IF NOT EXISTS status varchar; -- to avoid failure of the subsequent upgrade. -UPDATE alarm SET acknowledged = true, cleared = true WHERE status = 'CLEARED_ACK'; -UPDATE alarm SET acknowledged = true, cleared = false WHERE status = 'ACTIVE_ACK'; -UPDATE alarm SET acknowledged = false, cleared = true WHERE status = 'CLEARED_UNACK'; -UPDATE alarm SET acknowledged = false, cleared = false WHERE status = 'ACTIVE_UNACK'; - --- Drop index by 'status' column and replace with new indexes that has only active alarms; -DROP INDEX IF EXISTS idx_alarm_originator_alarm_type_active; -CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type_active - ON alarm USING btree (originator_id, type) WHERE cleared = false; - -DROP INDEX IF EXISTS idx_alarm_tenant_alarm_type_active; -CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_active - ON alarm USING btree (tenant_id, type) WHERE cleared = false; - --- Cover index by alarm type to optimize propagated alarm queries; -DROP INDEX IF EXISTS idx_entity_alarm_entity_id_alarm_type_created_time_alarm_id; -CREATE INDEX IF NOT EXISTS idx_entity_alarm_entity_id_alarm_type_created_time_alarm_id ON entity_alarm -USING btree (tenant_id, entity_id, alarm_type, created_time DESC) INCLUDE(alarm_id); - -DROP INDEX IF EXISTS idx_alarm_tenant_status_created_time; -ALTER TABLE alarm DROP COLUMN IF EXISTS status; - --- Update old alarms and set their state to clear, if there are newer alarms. -UPDATE alarm a -SET cleared = TRUE -WHERE cleared = FALSE - AND id != (SELECT l.id - FROM alarm l - WHERE l.tenant_id = a.tenant_id - AND l.originator_id = a.originator_id - AND l.type = a.type - ORDER BY l.created_time DESC, l.id - LIMIT 1); - --- ALARM STATUS REFACTORING END - --- ALARM COMMENTS START - -CREATE TABLE IF NOT EXISTS alarm_comment ( - id uuid NOT NULL, - created_time bigint NOT NULL, - alarm_id uuid NOT NULL, - user_id uuid, - type varchar(255) NOT NULL, - comment varchar(10000), - CONSTRAINT fk_alarm_comment_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE -) PARTITION BY RANGE (created_time); -CREATE INDEX IF NOT EXISTS idx_alarm_comment_alarm_id ON alarm_comment(alarm_id); - --- ALARM COMMENTS END - --- NOTIFICATIONS START - -CREATE TABLE IF NOT EXISTS notification_target ( - id UUID NOT NULL CONSTRAINT notification_target_pkey PRIMARY KEY, - created_time BIGINT NOT NULL, - tenant_id UUID NOT NULL, - name VARCHAR(255) NOT NULL, - configuration VARCHAR(10000) NOT NULL, - CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) -); -CREATE INDEX IF NOT EXISTS idx_notification_target_tenant_id_created_time ON notification_target(tenant_id, created_time DESC); - -CREATE TABLE IF NOT EXISTS notification_template ( - id UUID NOT NULL CONSTRAINT notification_template_pkey PRIMARY KEY, - created_time BIGINT NOT NULL, - tenant_id UUID NOT NULL, - name VARCHAR(255) NOT NULL, - notification_type VARCHAR(50) NOT NULL, - configuration VARCHAR(10000000) NOT NULL, - CONSTRAINT uq_notification_template_name UNIQUE (tenant_id, name) -); -CREATE INDEX IF NOT EXISTS idx_notification_template_tenant_id_created_time ON notification_template(tenant_id, created_time DESC); - -CREATE TABLE IF NOT EXISTS notification_rule ( - id UUID NOT NULL CONSTRAINT notification_rule_pkey PRIMARY KEY, - created_time BIGINT NOT NULL, - tenant_id UUID NOT NULL, - name VARCHAR(255) NOT NULL, - template_id UUID NOT NULL CONSTRAINT fk_notification_rule_template_id REFERENCES notification_template(id), - trigger_type VARCHAR(50) NOT NULL, - trigger_config VARCHAR(1000) NOT NULL, - recipients_config VARCHAR(10000) NOT NULL, - additional_config VARCHAR(255), - CONSTRAINT uq_notification_rule_name UNIQUE (tenant_id, name) -); -CREATE INDEX IF NOT EXISTS idx_notification_rule_tenant_id_trigger_type_created_time ON notification_rule(tenant_id, trigger_type, created_time DESC); - -CREATE TABLE IF NOT EXISTS notification_request ( - id UUID NOT NULL CONSTRAINT notification_request_pkey PRIMARY KEY, - created_time BIGINT NOT NULL, - tenant_id UUID NOT NULL, - targets VARCHAR(10000) NOT NULL, - template_id UUID, - template VARCHAR(10000000), - info VARCHAR(1000), - additional_config VARCHAR(1000), - originator_entity_id UUID, - originator_entity_type VARCHAR(32), - rule_id UUID NULL, - status VARCHAR(32), - stats VARCHAR(10000) -); -CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_user_created_time ON notification_request(tenant_id, created_time DESC) - WHERE originator_entity_type = 'USER'; -CREATE INDEX IF NOT EXISTS idx_notification_request_rule_id_originator_entity_id ON notification_request(rule_id, originator_entity_id) - WHERE originator_entity_type = 'ALARM'; -CREATE INDEX IF NOT EXISTS idx_notification_request_status ON notification_request(status) - WHERE status = 'SCHEDULED'; - -CREATE TABLE IF NOT EXISTS notification ( - id UUID NOT NULL, - created_time BIGINT NOT NULL, - request_id UUID NULL CONSTRAINT fk_notification_request_id REFERENCES notification_request(id) ON DELETE CASCADE, - recipient_id UUID NOT NULL CONSTRAINT fk_notification_recipient_id REFERENCES tb_user(id) ON DELETE CASCADE, - type VARCHAR(50) NOT NULL, - subject VARCHAR(255), - body VARCHAR(1000) NOT NULL, - additional_config VARCHAR(1000), - status VARCHAR(32) -) PARTITION BY RANGE (created_time); -CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); -CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC); - --- NOTIFICATIONS END - -ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS phone VARCHAR(255); - -CREATE TABLE IF NOT EXISTS user_settings ( - user_id uuid NOT NULL, - type VARCHAR(50) NOT NULL, - settings varchar(10000), - CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE, - CONSTRAINT user_settings_pkey PRIMARY KEY (user_id, type) -); - --- TTL DROP PARTITIONS FUNCTIONS UPDATE START - -DROP PROCEDURE IF EXISTS drop_partitions_by_max_ttl(character varying, bigint, bigint); -DROP FUNCTION IF EXISTS get_partition_by_max_ttl_date; - -CREATE OR REPLACE FUNCTION get_partition_by_system_ttl_date(IN partition_type varchar, IN date timestamp, OUT partition varchar) AS -$$ -BEGIN - CASE - WHEN partition_type = 'DAYS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM') || '_' || to_char(date, 'dd'); - WHEN partition_type = 'MONTHS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); - WHEN partition_type = 'YEARS' THEN - partition := 'ts_kv_' || to_char(date, 'yyyy'); - ELSE - partition := NULL; - END CASE; - IF partition IS NOT NULL THEN - IF NOT EXISTS(SELECT - FROM pg_tables - WHERE schemaname = 'public' - AND tablename = partition) THEN - partition := NULL; - RAISE NOTICE 'Failed to found partition by ttl'; - END IF; - END IF; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE PROCEDURE drop_partitions_by_system_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint) - LANGUAGE plpgsql AS -$$ -DECLARE - date timestamp; - partition_by_max_ttl_date varchar; - partition_by_max_ttl_month varchar; - partition_by_max_ttl_day varchar; - partition_by_max_ttl_year varchar; - partition varchar; - partition_year integer; - partition_month integer; - partition_day integer; - -BEGIN - if system_ttl IS NOT NULL AND system_ttl > 0 THEN - date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - system_ttl); - partition_by_max_ttl_date := get_partition_by_system_ttl_date(partition_type, date); - RAISE NOTICE 'Date by max ttl: %', date; - RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; - IF partition_by_max_ttl_date IS NOT NULL THEN - CASE - WHEN partition_type = 'DAYS' THEN - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); - WHEN partition_type = 'MONTHS' THEN - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); - ELSE - partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); - END CASE; - IF partition_by_max_ttl_year IS NULL THEN - RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!'; - ELSE - IF partition_type = 'YEARS' THEN - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END LOOP; - ELSE - IF partition_type = 'MONTHS' THEN - IF partition_by_max_ttl_month IS NULL THEN - RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!'; - ELSE - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year > partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_month := SPLIT_PART(partition, '_', 4)::integer; - IF partition_year = partition_by_max_ttl_year::integer THEN - IF partition_month >= partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END IF; - END IF; - END IF; - END LOOP; - END IF; - ELSE - IF partition_type = 'DAYS' THEN - IF partition_by_max_ttl_month IS NULL THEN - RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!'; - ELSE - IF partition_by_max_ttl_day IS NULL THEN - RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!'; - ELSE - FOR partition IN SELECT tablename - FROM pg_tables - WHERE schemaname = 'public' - AND tablename like 'ts_kv_' || '%' - AND tablename != 'ts_kv_latest' - AND tablename != 'ts_kv_dictionary' - AND tablename != 'ts_kv_indefinite' - AND tablename != partition_by_max_ttl_date - LOOP - partition_year := SPLIT_PART(partition, '_', 3)::integer; - IF partition_year > partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_year < partition_by_max_ttl_year::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_month := SPLIT_PART(partition, '_', 4)::integer; - IF partition_month > partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_month < partition_by_max_ttl_month::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - ELSE - partition_day := SPLIT_PART(partition, '_', 5)::integer; - IF partition_day >= partition_by_max_ttl_day::integer THEN - RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition; - CONTINUE; - ELSE - IF partition_day < partition_by_max_ttl_day::integer THEN - RAISE NOTICE 'Partition to delete by max ttl: %', partition; - EXECUTE format('DROP TABLE IF EXISTS %I', partition); - deleted := deleted + 1; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END LOOP; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; - END IF; -END -$$; - --- TTL DROP PARTITIONS FUNCTIONS UPDATE END - --- RULE NODE SINGLETON MODE SUPPORT - -ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS singleton_mode bool DEFAULT false; - -UPDATE rule_node SET singleton_mode = true WHERE type IN ('org.thingsboard.rule.engine.mqtt.azure.TbAzureIotHubNode', 'org.thingsboard.rule.engine.mqtt.TbMqttNode'); - -ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS clustering_mode varchar(255) DEFAULT 'ENABLED'; - -UPDATE component_descriptor SET clustering_mode = 'USER_PREFERENCE' WHERE clazz = 'org.thingsboard.rule.engine.mqtt.TbMqttNode'; - -UPDATE component_descriptor SET clustering_mode = 'SINGLETON' WHERE clazz = 'org.thingsboard.rule.engine.mqtt.azure.TbAzureIotHubNode'; - diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 58bd82abb3..f1c0260124 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -66,7 +66,6 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNoti import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; @@ -90,7 +89,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; +import org.thingsboard.server.gen.transport.TransportProtos.UplinkNotificationMsg; import org.thingsboard.server.service.rpc.RpcSubmitStrategy; +import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import javax.annotation.Nullable; @@ -173,7 +174,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso private EdgeId findRelatedEdgeId() { List result = - systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON); + systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); if (result != null && result.size() > 0) { EntityRelation relationToEdge = result.get(0); if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) { @@ -212,8 +213,11 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (systemContext.isEdgesEnabled() && edgeId != null) { log.debug("[{}][{}] device is related to edge: [{}]. Saving RPC request: [{}][{}] to edge queue", tenantId, deviceId, edgeId.getId(), rpcId, requestId); try { - saveRpcRequestToEdgeQueue(request, requestId).get(); - sent = true; + if (systemContext.getEdgeService().isEdgeActiveAsync(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE).get()) { + saveRpcRequestToEdgeQueue(request, requestId).get(); + } else { + log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}. The Edge is currently offline or unreachable", tenantId, deviceId, edgeId.getId(), request); + } } catch (InterruptedException | ExecutionException e) { log.error("[{}][{}][{}] Failed to save RPC request to edge queue {}", tenantId, deviceId, edgeId.getId(), request, e); } @@ -470,7 +474,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso callback.onSuccess(); } - private void processUplinkNotificationMsg(SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) { + private void processUplinkNotificationMsg(SessionInfoProto sessionInfo, UplinkNotificationMsg uplinkNotificationMsg) { String nodeId = sessionInfo.getNodeId(); sessions.entrySet().stream() .filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC())) diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index e14ed8203b..870ccda60a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -16,7 +16,6 @@ package org.thingsboard.server.actors.ruleChain; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorRef; @@ -29,6 +28,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; import org.thingsboard.server.common.data.relation.EntityRelation; diff --git a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java index 7791c7ee76..0b859e7b06 100644 --- a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.TenantProfileNotFoundException; import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index ba4c2b1fbd..92441bf1cb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -48,7 +48,7 @@ import org.thingsboard.server.common.data.security.event.UserSessionInvalidation import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.model.ActivateUserRequest; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 8e0e958262..22a42064b5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -20,8 +20,6 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.postgresql.util.PSQLException; -import org.postgresql.util.ServerErrorMessage; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -378,16 +376,13 @@ public abstract class BaseController { } else if (exception instanceof AsyncRequestTimeoutException) { return new ThingsboardException("Request timeout", ThingsboardErrorCode.GENERAL); } else if (exception instanceof DataAccessException) { - Throwable rootCause = ExceptionUtils.getRootCause(exception); - if (rootCause instanceof PSQLException) { - return new ThingsboardException(Optional.ofNullable(((PSQLException) rootCause).getServerErrorMessage()) - .map(ServerErrorMessage::getMessage).orElse(rootCause.getMessage()), ThingsboardErrorCode.GENERAL); - } else { - return new ThingsboardException(rootCause, ThingsboardErrorCode.GENERAL); + String errorType = exception.getClass().getSimpleName(); + if (!logControllerErrorStackTrace) { // not to log the error twice + log.warn("Database error: {} - {}", errorType, ExceptionUtils.getRootCauseMessage(exception)); } - } else { - return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL); + return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL); } + return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL); } /** diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index 1696d5c20b..64536ddb54 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -43,7 +43,7 @@ import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 85a6e061d0..44d81902a8 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -24,14 +24,12 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.install.DatabaseEntitiesUpgradeService; -import org.thingsboard.server.service.install.DatabaseTsUpgradeService; import org.thingsboard.server.service.install.EntityDatabaseSchemaService; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.NoSqlKeyspaceService; import org.thingsboard.server.service.install.SystemDataLoaderService; import org.thingsboard.server.service.install.TsDatabaseSchemaService; import org.thingsboard.server.service.install.TsLatestDatabaseSchemaService; -import org.thingsboard.server.service.install.migrate.EntitiesMigrateService; import org.thingsboard.server.service.install.migrate.TsLatestMigrateService; import org.thingsboard.server.service.install.update.CacheCleanupService; import org.thingsboard.server.service.install.update.DataUpdateService; @@ -70,9 +68,6 @@ public class ThingsboardInstallService { @Autowired private DatabaseEntitiesUpgradeService databaseEntitiesUpgradeService; - @Autowired(required = false) - private DatabaseTsUpgradeService databaseTsUpgradeService; - @Autowired private ComponentDiscoveryService componentDiscoveryService; @@ -88,9 +83,6 @@ public class ThingsboardInstallService { @Autowired private CacheCleanupService cacheCleanupService; - @Autowired(required = false) - private EntitiesMigrateService entitiesMigrateService; - @Autowired(required = false) private TsLatestMigrateService latestMigrateService; @@ -104,158 +96,13 @@ public class ThingsboardInstallService { cacheCleanupService.clearCache(upgradeFromVersion); - if ("2.5.0-cassandra".equals(upgradeFromVersion)) { - log.info("Migrating ThingsBoard entities data from cassandra to SQL database ..."); - entitiesMigrateService.migrate(); - log.info("Updating system data..."); - systemDataLoaderService.loadSystemWidgets(); - } else if ("3.0.1-cassandra".equals(upgradeFromVersion)) { + if ("cassandra-latest-to-postgres".equals(upgradeFromVersion)) { log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ..."); latestMigrateService.migrate(); } else if (upgradeFromVersion.equals("3.6.2-images")) { installScripts.updateImages(); } else { switch (upgradeFromVersion) { - case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion - log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("1.2.3"); - - case "1.3.0": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion - log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("1.3.0"); - - case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion - log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("1.3.1"); - - case "1.4.0": - log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("1.4.0"); - - dataUpdateService.updateData("1.4.0"); - - case "2.0.0": - log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.0.0"); - - case "2.1.1": - log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.1.1"); - case "2.1.3": - log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.1.3"); - - case "2.3.0": - log.info("Upgrading ThingsBoard from version 2.3.0 to 2.3.1 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.3.0"); - - case "2.3.1": - log.info("Upgrading ThingsBoard from version 2.3.1 to 2.4.0 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.3.1"); - - case "2.4.0": - log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ..."); - - case "2.4.1": - log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.4.1"); - case "2.4.2": - log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ..."); - - databaseEntitiesUpgradeService.upgradeDatabase("2.4.2"); - - case "2.4.3": - log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ..."); - - if (databaseTsUpgradeService != null) { - databaseTsUpgradeService.upgradeDatabase("2.4.3"); - } - databaseEntitiesUpgradeService.upgradeDatabase("2.4.3"); - case "2.5.0": - log.info("Upgrading ThingsBoard from version 2.5.0 to 2.5.1 ..."); - if (databaseTsUpgradeService != null) { - databaseTsUpgradeService.upgradeDatabase("2.5.0"); - } - case "2.5.1": - log.info("Upgrading ThingsBoard from version 2.5.1 to 3.0.0 ..."); - case "3.0.1": - log.info("Upgrading ThingsBoard from version 3.0.1 to 3.1.0 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.0.1"); - dataUpdateService.updateData("3.0.1"); - case "3.1.0": - log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); - case "3.1.1": - log.info("Upgrading ThingsBoard from version 3.1.1 to 3.2.0 ..."); - if (databaseTsUpgradeService != null) { - databaseTsUpgradeService.upgradeDatabase("3.1.1"); - } - databaseEntitiesUpgradeService.upgradeDatabase("3.1.1"); - dataUpdateService.updateData("3.1.1"); - systemDataLoaderService.createOAuth2Templates(); - case "3.2.0": - log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); - case "3.2.1": - log.info("Upgrading ThingsBoard from version 3.2.1 to 3.2.2 ..."); - if (databaseTsUpgradeService != null) { - databaseTsUpgradeService.upgradeDatabase("3.2.1"); - } - databaseEntitiesUpgradeService.upgradeDatabase("3.2.1"); - case "3.2.2": - log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ..."); - if (databaseTsUpgradeService != null) { - databaseTsUpgradeService.upgradeDatabase("3.2.2"); - } - databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); - - dataUpdateService.updateData("3.2.2"); - systemDataLoaderService.createOAuth2Templates(); - case "3.3.0": - log.info("Upgrading ThingsBoard from version 3.3.0 to 3.3.1 ..."); - case "3.3.1": - log.info("Upgrading ThingsBoard from version 3.3.1 to 3.3.2 ..."); - case "3.3.2": - log.info("Upgrading ThingsBoard from version 3.3.2 to 3.3.3 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.3.2"); - dataUpdateService.updateData("3.3.2"); - case "3.3.3": - log.info("Upgrading ThingsBoard from version 3.3.3 to 3.3.4 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.3.3"); - case "3.3.4": - log.info("Upgrading ThingsBoard from version 3.3.4 to 3.4.0 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.3.4"); - dataUpdateService.updateData("3.3.4"); - case "3.4.0": - log.info("Upgrading ThingsBoard from version 3.4.0 to 3.4.1 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.4.0"); - dataUpdateService.updateData("3.4.0"); - case "3.4.1": - log.info("Upgrading ThingsBoard from version 3.4.1 to 3.4.2 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.4.1"); - dataUpdateService.updateData("3.4.1"); - case "3.4.2": - log.info("Upgrading ThingsBoard from version 3.4.2 to 3.4.3 ..."); - case "3.4.3": - log.info("Upgrading ThingsBoard from version 3.4.3 to 3.4.4 ..."); - case "3.4.4": - log.info("Upgrading ThingsBoard from version 3.4.4 to 3.5.0 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.4.4"); - if (!getEnv("SKIP_DEFAULT_NOTIFICATION_CONFIGS_CREATION", false)) { - systemDataLoaderService.createDefaultNotificationConfigs(); - } else { - log.info("Skipping default notification configs creation"); - } case "3.5.0": log.info("Upgrading ThingsBoard from version 3.5.0 to 3.5.1 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.5.0"); @@ -279,6 +126,7 @@ public class ThingsboardInstallService { case "3.6.2": log.info("Upgrading ThingsBoard from version 3.6.2 to 3.6.3 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.6.2"); + systemDataLoaderService.updateDefaultNotificationConfigs(); //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; default: diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index a10a48244b..2f157af70e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; @@ -149,6 +150,9 @@ public class EdgeContextComponent { @Autowired private ResourceService resourceService; + @Autowired + private NotificationRuleProcessor notificationRuleProcessor; + @Autowired private AlarmEdgeProcessor alarmProcessor; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index d3262039d5..026ca51e8d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeConnectionTrigger; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -263,7 +264,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { - TenantId tenantId = edgeGrpcSession.getEdge().getTenantId(); + Edge edge = edgeGrpcSession.getEdge(); + TenantId tenantId = edge.getTenantId(); log.info("[{}][{}] edge [{}] connected successfully.", tenantId, edgeGrpcSession.getSessionId(), edgeId); sessions.put(edgeId, edgeGrpcSession); final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); @@ -276,7 +278,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); long lastConnectTs = System.currentTimeMillis(); save(tenantId, edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs); - pushRuleEngineMessage(tenantId, edgeId, lastConnectTs, TbMsgType.CONNECT_EVENT); + pushRuleEngineMessage(tenantId, edge, lastConnectTs, TbMsgType.CONNECT_EVENT); cancelScheduleEdgeEventsCheck(edgeId); scheduleEdgeEventsCheck(edgeGrpcSession); } @@ -381,7 +383,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } } - private void onEdgeDisconnect(EdgeId edgeId, UUID sessionId) { + private void onEdgeDisconnect(Edge edge, UUID sessionId) { + EdgeId edgeId = edge.getId(); log.info("[{}][{}] edge disconnected!", edgeId, sessionId); EdgeGrpcSession toRemove = sessions.get(edgeId); if (toRemove.getSessionId().equals(sessionId)) { @@ -397,7 +400,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); long lastDisconnectTs = System.currentTimeMillis(); save(tenantId, edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs); - pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT); + pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edge, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT); cancelScheduleEdgeEventsCheck(edgeId); } else { log.debug("[{}] edge session [{}] is not available anymore, nothing to remove. most probably this session is already outdated!", edgeId, sessionId); @@ -452,25 +455,36 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } } - private void pushRuleEngineMessage(TenantId tenantId, EdgeId edgeId, long ts, TbMsgType msgType) { + private void pushRuleEngineMessage(TenantId tenantId, Edge edge, long ts, TbMsgType msgType) { try { + EdgeId edgeId = edge.getId(); ObjectNode edgeState = JacksonUtil.newObjectNode(); - if (msgType.equals(TbMsgType.CONNECT_EVENT)) { + boolean isConnected = TbMsgType.CONNECT_EVENT.equals(msgType); + if (isConnected) { edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, true); edgeState.put(DefaultDeviceStateService.LAST_CONNECT_TIME, ts); } else { edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, false); edgeState.put(DefaultDeviceStateService.LAST_DISCONNECT_TIME, ts); } + ctx.getNotificationRuleProcessor().process(EdgeConnectionTrigger.builder() + .tenantId(tenantId) + .customerId(edge.getCustomerId()) + .edgeId(edgeId) + .edgeName(edge.getName()) + .connected(isConnected).build()); String data = JacksonUtil.toString(edgeState); TbMsgMetaData md = new TbMsgMetaData(); if (!persistToTelemetry) { md.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE); + md.putValue("edgeName", edge.getName()); + md.putValue("edgeType", edge.getType()); } TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data); clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null); } catch (Exception e) { - log.warn("[{}][{}] Failed to push {}", tenantId, edgeId, msgType, e); + log.warn("[{}][{}] Failed to push {}", tenantId, edge.getId(), msgType, e); } } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 5e6f7a263e..98346674ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -111,7 +112,7 @@ public final class EdgeGrpcSession implements Closeable { private final UUID sessionId; private final BiConsumer sessionOpenListener; - private final BiConsumer sessionCloseListener; + private final BiConsumer sessionCloseListener; private final EdgeSessionState sessionState = new EdgeSessionState(); @@ -137,7 +138,7 @@ public final class EdgeGrpcSession implements Closeable { private ScheduledExecutorService sendDownlinkExecutorService; EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener, - BiConsumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize) { + BiConsumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize) { this.sessionId = UUID.randomUUID(); this.ctx = ctx; this.outputStream = outputStream; @@ -206,7 +207,7 @@ public final class EdgeGrpcSession implements Closeable { connected = false; if (edge != null) { try { - sessionCloseListener.accept(edge.getId(), sessionId); + sessionCloseListener.accept(edge, sessionId); } catch (Exception ignored) { } } @@ -314,7 +315,7 @@ public final class EdgeGrpcSession implements Closeable { } catch (Exception e) { log.error("[{}][{}] Failed to send downlink message [{}]", this.tenantId, this.sessionId, downlinkMsg, e); connected = false; - sessionCloseListener.accept(edge.getId(), sessionId); + sessionCloseListener.accept(edge, sessionId); } finally { downlinkMsgLock.unlock(); } @@ -466,15 +467,26 @@ public final class EdgeGrpcSession implements Closeable { if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) { List copy = new ArrayList<>(sessionState.getPendingMsgsMap().values()); if (attempt > 1) { - log.warn("[{}][{}] Failed to deliver the batch: {}, attempt: {}", this.tenantId, this.sessionId, copy, attempt); + String error = "Failed to deliver the batch"; + String failureMsg = String.format("{%s}: {%s}", error, copy); + if (attempt == 2) { + // Send a failure notification only on the second attempt. + // This ensures that failure alerts are sent just once to avoid redundant notifications. + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId) + .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build()); + } + log.warn("[{}][{}] {}, attempt: {}", this.tenantId, this.sessionId, failureMsg, attempt); } log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size()); for (DownlinkMsg downlinkMsg : copy) { if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) { - log.error("[{}][{}][{}] Downlink msg size [{}] exceeds client max inbound message size [{}]. Skipping this message. " + - "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it." + - "Message {}", this.tenantId, edge.getId(), this.sessionId, downlinkMsg.getSerializedSize(), - this.clientMaxInboundMessageSize, downlinkMsg); + String error = String.format("Client max inbound message size [{%s}] is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + + "env variable on the edge and restart it.", this.clientMaxInboundMessageSize); + String message = String.format("Downlink msg size [{%s}] exceeds client max inbound message size [{%s}]. " + + "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it.", downlinkMsg.getSerializedSize(), this.clientMaxInboundMessageSize); + log.error("[{}][{}][{}] {} Message {}", this.tenantId, edge.getId(), this.sessionId, message, downlinkMsg); + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId) + .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(message).error(error).build()); sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId()); } else { sendDownlinkMsg(ResponseMsg.newBuilder() @@ -485,8 +497,12 @@ public final class EdgeGrpcSession implements Closeable { if (attempt < MAX_DOWNLINK_ATTEMPTS) { scheduleDownlinkMsgsPackSend(attempt + 1); } else { + String failureMsg = String.format("Failed to deliver messages: %s", copy); log.warn("[{}][{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}", this.tenantId, this.sessionId, MAX_DOWNLINK_ATTEMPTS, copy); + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg) + .error("Failed to deliver messages after " + MAX_DOWNLINK_ATTEMPTS + " attempts").build()); stopCurrentSendDownlinkMsgsTask(false); } } else { @@ -791,7 +807,10 @@ public final class EdgeGrpcSession implements Closeable { } } } catch (Exception e) { + String failureMsg = String.format("Can't process uplink msg [%s] from edge", uplinkMsg); log.error("[{}][{}] Can't process uplink msg [{}]", this.tenantId, this.sessionId, uplinkMsg, e); + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(e.getMessage()).build()); return Futures.immediateFailedFuture(e); } return Futures.allAsList(result); @@ -815,15 +834,22 @@ public final class EdgeGrpcSession implements Closeable { .setMaxInboundMessageSize(maxInboundMessageSize) .build(); } + String error = "Failed to validate the edge!"; + String failureMsg = String.format("{%s} Provided request secret: %s", error, request.getEdgeSecret()); + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build()); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.BAD_CREDENTIALS) - .setErrorMsg("Failed to validate the edge!") + .setErrorMsg(failureMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } catch (Exception e) { - log.error("[{}] Failed to process edge connection!", request.getEdgeRoutingKey(), e); + String failureMsg = "Failed to process edge connection!"; + ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(e.getMessage()).build()); + log.error(failureMsg, e); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.SERVER_UNAVAILABLE) - .setErrorMsg("Failed to process edge connection!") + .setErrorMsg(failureMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index 11cd2ab56c..97e1590b5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -113,7 +113,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { } switch (alarmCommentUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: - alarmCommentDao.createAlarmComment(tenantId, alarmComment); + alarmCommentDao.save(tenantId, alarmComment); break; case ENTITY_UPDATED_RPC_MESSAGE: alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index 2a191dfaf4..118cc0ccd4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.processor.rule; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.RuleChain; @@ -53,6 +54,10 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { isRoot = Boolean.parseBoolean(edgeEvent.getBody().get(EDGE_IS_ROOT_BODY_KEY).asText()); } catch (Exception ignored) {} } + if (!isRoot) { + Edge edge = edgeService.findEdgeById(edgeEvent.getTenantId(), edgeEvent.getEdgeId()); + isRoot = edge.getRootRuleChainId().equals(ruleChainId); + } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor) ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java index cbef97d134..f58a55678a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java @@ -50,22 +50,15 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb public Queue saveQueue(Queue queue) { boolean create = queue.getId() == null; Queue oldQueue; - if (create) { oldQueue = null; } else { oldQueue = queueService.findQueueById(queue.getTenantId(), queue.getId()); } - //TODO: add checkNotNull Queue savedQueue = queueService.saveQueue(queue); - - if (create) { - onQueueCreated(savedQueue); - } else { - onQueueUpdated(savedQueue, oldQueue); - } - + createTopicsIfNeeded(savedQueue, oldQueue); + tbClusterService.onQueuesUpdate(List.of(savedQueue)); return savedQueue; } @@ -73,54 +66,14 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb public void deleteQueue(TenantId tenantId, QueueId queueId) { Queue queue = queueService.findQueueById(tenantId, queueId); queueService.deleteQueue(tenantId, queueId); - onQueueDeleted(queue); + tbClusterService.onQueuesDelete(List.of(queue)); } @Override public void deleteQueueByQueueName(TenantId tenantId, String queueName) { Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, queueName); queueService.deleteQueue(tenantId, queue.getId()); - onQueueDeleted(queue); - } - - private void onQueueCreated(Queue queue) { - for (int i = 0; i < queue.getPartitions(); i++) { - tbQueueAdmin.createTopicIfNotExists( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(), - queue.getCustomProperties() - ); - } - - tbClusterService.onQueueChange(queue); - } - - private void onQueueUpdated(Queue queue, Queue oldQueue) { - int oldPartitions = oldQueue.getPartitions(); - int currentPartitions = queue.getPartitions(); - - if (currentPartitions != oldPartitions) { - if (currentPartitions > oldPartitions) { - log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName()); - for (int i = oldPartitions; i < currentPartitions; i++) { - tbQueueAdmin.createTopicIfNotExists( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(), - queue.getCustomProperties() - ); - } - tbClusterService.onQueueChange(queue); - } else { - log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName()); - tbClusterService.onQueueChange(queue); - // TODO: move all the messages left in old partitions and delete topics - } - } else if (!oldQueue.equals(queue)) { - tbClusterService.onQueueChange(queue); - } - } - - private void onQueueDeleted(Queue queue) { - tbClusterService.onQueueDelete(queue); -// queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId); + tbClusterService.onQueuesDelete(List.of(queue)); } @Override @@ -176,26 +129,56 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb log.debug("[{}] Handling profile queue config update: creating queues {}, updating {}, deleting {}. Affected tenants: {}", newTenantProfile.getUuidId(), toCreate, toUpdate, toRemove, tenantIds); } - tenantIds.forEach(tenantId -> { - toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key)))); - toUpdate.forEach(key -> { - Queue queueToUpdate = new Queue(tenantId, newQueues.get(key)); - Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, key); - queueToUpdate.setId(foundQueue.getId()); - queueToUpdate.setCreatedTime(foundQueue.getCreatedTime()); + List updated = new ArrayList<>(); + List deleted = new ArrayList<>(); + for (TenantId tenantId : tenantIds) { + for (String name : toCreate) { + updated.add(new Queue(tenantId, newQueues.get(name))); + } - if (!queueToUpdate.equals(foundQueue)) { - saveQueue(queueToUpdate); + for (String name : toUpdate) { + Queue queue = new Queue(tenantId, newQueues.get(name)); + Queue foundQueue = queueService.findQueueByTenantIdAndName(tenantId, name); + if (foundQueue != null) { + queue.setId(foundQueue.getId()); + queue.setCreatedTime(foundQueue.getCreatedTime()); } - }); + if (!queue.equals(foundQueue)) { + updated.add(queue); + createTopicsIfNeeded(queue, foundQueue); + } + } + + for (String name : toRemove) { + Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, name); + deleted.add(queue); + } + } - toRemove.forEach(q -> { - Queue queue = queueService.findQueueByTenantIdAndNameInternal(tenantId, q); - QueueId queueIdForRemove = queue.getId(); - deleteQueue(tenantId, queueIdForRemove); + if (!updated.isEmpty()) { + updated = updated.stream() + .map(queueService::saveQueue) + .collect(Collectors.toList()); + tbClusterService.onQueuesUpdate(updated); + } + if (!deleted.isEmpty()) { + deleted.forEach(queue -> { + queueService.deleteQueue(queue.getTenantId(), queue.getId()); }); - }); + tbClusterService.onQueuesDelete(deleted); + } + } + + private void createTopicsIfNeeded(Queue queue, Queue oldQueue) { + int newPartitions = queue.getPartitions(); + int oldPartitions = oldQueue != null ? oldQueue.getPartitions() : 0; + for (int i = oldPartitions; i < newPartitions; i++) { + tbQueueAdmin.createTopicIfNotExists( + new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(), + queue.getCustomProperties() + ); + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java index c1297d7cfa..2937b71974 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.install; -import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -30,29 +29,6 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { - case "2.4.3": - log.info("Updating schema ..."); - String updateTsKvTableStmt = "alter table ts_kv_cf add json_v text"; - String updateTsKvLatestTableStmt = "alter table ts_kv_latest_cf add json_v text"; - - try { - log.info("Updating ts ..."); - cluster.getSession().execute(updateTsKvTableStmt); - Thread.sleep(2500); - log.info("Ts updated."); - log.info("Updating ts latest ..."); - cluster.getSession().execute(updateTsKvLatestTableStmt); - Thread.sleep(2500); - log.info("Ts latest updated."); - } catch (InvalidQueryException e) { - } - log.info("Schema updated."); - break; - case "2.5.0": - case "3.1.1": - case "3.2.1": - case "3.2.2": - break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java deleted file mode 100644 index 89a8dd9187..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install; - -import com.fasterxml.jackson.databind.JavaType; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.UUIDConverter; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.dashboard.DashboardService; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - -/** - * Created by igor on 2/27/18. - */ -@Slf4j -public class DatabaseHelper { - - public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); - - public static final String DEVICE = "device"; - public static final String ENTITY_ID = "entity_id"; - public static final String TENANT_ID = "tenant_id"; - public static final String ENTITY_TYPE = "entity_type"; - public static final String CUSTOMER_ID = "customer_id"; - public static final String SEARCH_TEXT = "search_text"; - public static final String ADDITIONAL_INFO = "additional_info"; - public static final String ASSET = "asset"; - public static final String DASHBOARD = "dashboard"; - public static final String ENTITY_VIEWS = "entity_views"; - public static final String ENTITY_VIEW = "entity_view"; - public static final String RULE_CHAIN = "rule_chain"; - public static final String ID = "id"; - public static final String TITLE = "title"; - public static final String TYPE = "type"; - public static final String NAME = "name"; - public static final String KEYS = "keys"; - public static final String START_TS = "start_ts"; - public static final String END_TS = "end_ts"; - public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; - public static final String CONFIGURATION = "configuration"; - - - public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { - JavaType assignedCustomersType = - JacksonUtil.constructCollectionType(HashSet.class, ShortCustomerInfo.class); - try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withFirstRecordAsHeader())) { - csvParser.forEach(record -> { - String customerIdString = record.get(CUSTOMER_ID); - String assignedCustomersString = record.get(ASSIGNED_CUSTOMERS); - DashboardId dashboardId = new DashboardId(toUUID(record.get(ID), sql)); - List customerIds = new ArrayList<>(); - if (!StringUtils.isEmpty(assignedCustomersString)) { - try { - Set assignedCustomers = JacksonUtil.fromString(assignedCustomersString, assignedCustomersType); - assignedCustomers.forEach((customerInfo) -> { - CustomerId customerId = customerInfo.getCustomerId(); - if (!customerId.isNullUid()) { - customerIds.add(customerId); - } - }); - } catch (IllegalArgumentException e) { - log.error("Unable to parse assigned customers field", e); - } - } - if (!StringUtils.isEmpty(customerIdString)) { - CustomerId customerId = new CustomerId(toUUID(customerIdString, sql)); - if (!customerId.isNullUid()) { - customerIds.add(customerId); - } - } - for (CustomerId customerId : customerIds) { - dashboardService.assignDashboardToCustomer(TenantId.SYS_TENANT_ID, dashboardId, customerId); - } - }); - } - } - - private static UUID toUUID(String src, boolean sql) { - if (sql) { - return UUIDConverter.fromString(src); - } else { - return UUID.fromString(src); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index ddc848a91d..b20a548d90 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -297,11 +297,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { jwtSettingsService.createRandomJwtSettings(); } - @Override - public void saveLegacyYmlSettings() throws Exception { - jwtSettingsService.saveLegacyYmlSettings(); - } - @Override public void createOAuth2Templates() throws Exception { installScripts.createOAuth2Templates(); @@ -696,7 +691,23 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } @Override + @SneakyThrows public void updateDefaultNotificationConfigs() { + PageDataIterable tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500); + ExecutorService executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4)); + log.info("Updating default edge failure notification configs for all tenants"); + AtomicInteger count = new AtomicInteger(); + for (TenantId tenantId : tenants) { + executor.submit(() -> { + notificationSettingsService.updateDefaultNotificationConfigs(tenantId); + int n = count.incrementAndGet(); + if (n % 500 == 0) { + log.info("{} tenants processed", n); + } + }); + } + executor.shutdown(); + executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); notificationSettingsService.updateDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 1b42ebcb8d..436c2fcc84 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -15,37 +15,11 @@ */ package org.thingsboard.server.service.install; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntitySubtype; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.queue.ProcessingStrategy; -import org.thingsboard.server.common.data.queue.ProcessingStrategyType; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.data.queue.SubmitStrategy; -import org.thingsboard.server.common.data.queue.SubmitStrategyType; -import org.thingsboard.server.common.data.util.TbPair; -import org.thingsboard.server.dao.asset.AssetDao; -import org.thingsboard.server.dao.asset.AssetProfileService; -import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.device.DeviceProfileService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.queue.QueueService; -import org.thingsboard.server.dao.sql.tenant.TenantRepository; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; -import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; -import org.thingsboard.server.service.install.sql.SqlDbHelper; import org.thingsboard.server.service.install.update.DefaultDataUpdateService; import java.nio.charset.Charset; @@ -56,34 +30,11 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLSyntaxErrorException; import java.sql.SQLWarning; import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; -import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; -import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; -import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; -import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; -import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; -import static org.thingsboard.server.service.install.DatabaseHelper.ID; -import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; -import static org.thingsboard.server.service.install.DatabaseHelper.NAME; -import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; -import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; -import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; -import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; -import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; - @Service @Profile("install") @Slf4j @@ -100,623 +51,12 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @Value("${spring.datasource.password}") private String dbPassword; - @Autowired - private DashboardService dashboardService; - @Autowired private InstallScripts installScripts; - @Autowired - private SystemDataLoaderService systemDataLoaderService; - - @Autowired - private TenantService tenantService; - - @Autowired - private TenantRepository tenantRepository; - - @Autowired - private DeviceService deviceService; - - @Autowired - private AssetDao assetDao; - - @Autowired - private DeviceProfileService deviceProfileService; - - @Autowired - private AssetProfileService assetProfileService; - - @Autowired - private ApiUsageStateService apiUsageStateService; - - @Lazy - @Autowired - private QueueService queueService; - - @Autowired - private TbRuleEngineQueueConfigService queueConfig; - - @Autowired - private DbUpgradeExecutorService dbUpgradeExecutor; - @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { - case "1.3.0": - log.info("Updating schema ..."); - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.1", SCHEMA_UPDATE_SQL); - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - loadSql(schemaUpdateFile, conn); - } - log.info("Schema updated."); - break; - case "1.3.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - - log.info("Dumping dashboards ..."); - Path dashboardsDump = SqlDbHelper.dumpTableIfExists(conn, DASHBOARD, - new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, - new String[]{"", "", "", "", "", "", ""}, - "tb-dashboards", true); - log.info("Dashboards dumped."); - - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - - log.info("Restoring dashboards ..."); - if (dashboardsDump != null) { - SqlDbHelper.loadTable(conn, DASHBOARD, - new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump, true); - DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, true); - Files.deleteIfExists(dashboardsDump); - } - log.info("Dashboards restored."); - } - break; - case "1.4.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - } - break; - case "2.0.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - } - break; - case "2.1.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - - log.info("Dumping entity views ..."); - Path entityViewsDump = SqlDbHelper.dumpTableIfExists(conn, ENTITY_VIEWS, - new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, - new String[]{"", "", "", "", "", "default", "", "", "0", "0", "", ""}, - "tb-entity-views", true); - log.info("Entity views dumped."); - - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - - log.info("Restoring entity views ..."); - if (entityViewsDump != null) { - SqlDbHelper.loadTable(conn, ENTITY_VIEW, - new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump, true); - Files.deleteIfExists(entityViewsDump); - } - log.info("Entity views restored."); - } - break; - case "2.1.3": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.2.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - } - break; - case "2.3.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.3.1", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - } - break; - case "2.3.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - try { - conn.createStatement().execute("ALTER TABLE device ADD COLUMN label varchar(255)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - log.info("Schema updated."); - } - break; - case "2.4.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("ALTER TABLE asset ADD COLUMN label varchar(255)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.2", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - try { - conn.createStatement().execute("ALTER TABLE device ADD CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - try { - conn.createStatement().execute("ALTER TABLE device_credentials ADD CONSTRAINT device_credentials_id_unq_key UNIQUE (credentials_id)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - try { - conn.createStatement().execute("ALTER TABLE asset ADD CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - log.info("Schema updated."); - } - break; - case "2.4.2": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_relation_types varchar"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - log.info("Schema updated."); - } - break; - case "2.4.3": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); - } catch (Exception e) { - if (e instanceof SQLSyntaxErrorException) { - try { - conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v varchar(10000000);"); - } catch (Exception e1) { - } - } - } - try { - conn.createStatement().execute("ALTER TABLE tenant ADD COLUMN isolated_tb_core boolean DEFAULT (false), ADD COLUMN isolated_tb_rule_engine boolean DEFAULT (false)"); - } catch (Exception e) { - } - try { - long ts = System.currentTimeMillis(); - conn.createStatement().execute("ALTER TABLE event ADD COLUMN ts bigint DEFAULT " + ts + ";"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - log.info("Schema updated."); - } - break; - case "3.0.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3000001)) { - String[] tables = new String[]{"admin_settings", "alarm", "asset", "audit_log", "attribute_kv", - "component_descriptor", "customer", "dashboard", "device", "device_credentials", "event", - "relation", "tb_user", "tenant", "user_credentials", "widget_type", "widgets_bundle", - "rule_chain", "rule_node", "entity_view"}; - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.0.1", "schema_update_to_uuid.sql"); - loadSql(schemaUpdateFile, conn); - - conn.createStatement().execute("call drop_all_idx()"); - - log.info("Optimizing alarm relations..."); - conn.createStatement().execute("DELETE from relation WHERE relation_type_group = 'ALARM' AND relation_type <> 'ALARM_ANY';"); - conn.createStatement().execute("DELETE from relation WHERE relation_type_group = 'ALARM' AND relation_type = 'ALARM_ANY' " + - "AND exists(SELECT * FROM alarm WHERE alarm.id = relation.to_id AND alarm.originator_id = relation.from_id)"); - log.info("Alarm relations optimized."); - - for (String table : tables) { - log.info("Updating table {}.", table); - Statement statement = conn.createStatement(); - statement.execute("call update_" + table + "();"); - - SQLWarning warnings = statement.getWarnings(); - if (warnings != null) { - log.info("{}", warnings.getMessage()); - SQLWarning nextWarning = warnings.getNextWarning(); - while (nextWarning != null) { - log.info("{}", nextWarning.getMessage()); - nextWarning = nextWarning.getNextWarning(); - } - } - - conn.createStatement().execute("DROP PROCEDURE update_" + table); - log.info("Table {} updated.", table); - } - conn.createStatement().execute("call create_all_idx()"); - - conn.createStatement().execute("DROP PROCEDURE drop_all_idx"); - conn.createStatement().execute("DROP PROCEDURE create_all_idx"); - conn.createStatement().execute("DROP FUNCTION column_type_to_uuid"); - - log.info("Updating alarm relations..."); - conn.createStatement().execute("UPDATE relation SET relation_type = 'ANY' WHERE relation_type_group = 'ALARM' AND relation_type = 'ALARM_ANY';"); - log.info("Alarm relations updated."); - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3001000;"); - - conn.createStatement().execute("VACUUM FULL"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.1.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Schema updated."); - } - break; - case "3.1.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3001000)) { - - try { - conn.createStatement().execute("ALTER TABLE device ADD COLUMN device_profile_id uuid, ADD COLUMN device_data jsonb"); - } catch (Exception e) { - } - - try { - conn.createStatement().execute("ALTER TABLE tenant ADD COLUMN tenant_profile_id uuid"); - } catch (Exception e) { - } - - try { - conn.createStatement().execute("CREATE TABLE IF NOT EXISTS rule_node_state (" + - " id uuid NOT NULL CONSTRAINT rule_node_state_pkey PRIMARY KEY," + - " created_time bigint NOT NULL," + - " rule_node_id uuid NOT NULL," + - " entity_type varchar(32) NOT NULL," + - " entity_id uuid NOT NULL," + - " state_data varchar(16384) NOT NULL," + - " CONSTRAINT rule_node_state_unq_key UNIQUE (rule_node_id, entity_id)," + - " CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE)"); - } catch (Exception e) { - } - - try { - conn.createStatement().execute("CREATE TABLE IF NOT EXISTS api_usage_state (" + - " id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY," + - " created_time bigint NOT NULL," + - " tenant_id uuid," + - " entity_type varchar(32)," + - " entity_id uuid," + - " transport varchar(32)," + - " db_storage varchar(32)," + - " re_exec varchar(32)," + - " js_exec varchar(32)," + - " email_exec varchar(32)," + - " sms_exec varchar(32)," + - " CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" + - ");"); - } catch (Exception e) { - } - - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql"); - loadSql(schemaUpdateFile, conn); - - log.info("Creating default tenant profiles..."); - systemDataLoaderService.createDefaultTenantProfiles(); - - log.info("Updating tenant profiles..."); - conn.createStatement().execute("call update_tenant_profiles()"); - - log.info("Creating default device profiles..."); - PageLink pageLink = new PageLink(100); - PageData pageData; - do { - pageData = tenantService.findTenants(pageLink); - for (Tenant tenant : pageData.getData()) { - try { - apiUsageStateService.createDefaultApiUsageState(tenant.getId(), null); - } catch (Exception e) { - } - List deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get(); - try { - deviceProfileService.createDefaultDeviceProfile(tenant.getId()); - } catch (Exception e) { - } - for (EntitySubtype deviceType : deviceTypes) { - try { - deviceProfileService.findOrCreateDeviceProfile(tenant.getId(), deviceType.getType()); - } catch (Exception e) { - } - } - } - pageLink = pageLink.nextPageLink(); - } while (pageData.hasNext()); - - log.info("Updating device profiles..."); - conn.createStatement().execute("call update_device_profiles()"); - - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_after.sql"); - loadSql(schemaUpdateFile, conn); - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002000;"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.2.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_device_device_profile_id ON device(tenant_id, device_profile_id);"); - conn.createStatement().execute("ALTER TABLE dashboard ALTER COLUMN configuration TYPE varchar;"); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002001;"); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - log.info("Schema updated."); - } - break; - case "3.2.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time);"); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.1", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002002;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.2.2": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("ALTER TABLE rule_chain ADD COLUMN type varchar(255) DEFAULT 'CORE'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception ignored) { - } - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Load Edge TTL functions ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", "schema_update_ttl.sql"); - loadSql(schemaUpdateFile, conn); - log.info("Edge TTL functions successfully loaded!"); - log.info("Updating indexes and TTL procedure for event table..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.2.2", "schema_update_event.sql"); - loadSql(schemaUpdateFile, conn); - log.info("Updating schema settings..."); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003000;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.3.2": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - try { - conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_to_owner boolean DEFAULT false;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_to_tenant boolean DEFAULT false;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception ignored) { - } - - try { - conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" + - " select tenant_id, originator_id, created_time, type, customer_id, id from alarm ON CONFLICT DO NOTHING;"); - conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" + - " select a.tenant_id, r.from_id, created_time, type, customer_id, id" + - " from alarm a inner join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id ON CONFLICT DO NOTHING;"); - conn.createStatement().execute("delete from relation r where r.relation_type_group = 'ALARM';"); - } catch (Exception e) { - log.error("Failed to update alarm relations!!!", e); - } - - log.info("Updating lwm2m device profiles ..."); - try { - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", "schema_update_lwm2m_bootstrap.sql"); - loadSql(schemaUpdateFile, conn); - log.info("Updating server`s public key from HexDec to Base64 in profile for LWM2M..."); - conn.createStatement().execute("call update_profile_bootstrap();"); - log.info("Server`s public key from HexDec to Base64 in profile for LWM2M updated."); - log.info("Updating client`s public key and secret key from HexDec to Base64 for LWM2M..."); - conn.createStatement().execute("call update_device_credentials_to_base64_and_bootstrap();"); - log.info("Client`s public key and secret key from HexDec to Base64 for LWM2M updated."); - } catch (Exception e) { - log.error("Failed to update lwm2m profiles!!!", e); - } - log.info("Updating schema settings..."); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003003;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.3.3": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - try { - conn.createStatement().execute("ALTER TABLE edge DROP COLUMN edge_license_key;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - conn.createStatement().execute("ALTER TABLE edge DROP COLUMN cloud_endpoint;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception ignored) { - } - - log.info("Updating TTL cleanup procedure for the event table..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.3", "schema_event_ttl_procedure.sql"); - loadSql(schemaUpdateFile, conn); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.3", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - - log.info("Updating schema settings..."); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3003004;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.3.4": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.4", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - - log.info("Loading queues..."); - try { - if (!CollectionUtils.isEmpty(queueConfig.getQueues())) { - queueConfig.getQueues().forEach(queueSettings -> { - Queue queue = queueConfigToQueue(queueSettings); - Queue existing = queueService.findQueueByTenantIdAndName(queue.getTenantId(), queue.getName()); - if (existing == null) { - queueService.saveQueue(queue); - } - }); - } else { - systemDataLoaderService.createQueues(); - } - } catch (Exception e) { - } - - log.info("Updating schema settings..."); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.4.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - log.info("Updating schema settings..."); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004001;"); - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.4.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - runSchemaUpdateScript(conn, "3.4.1"); - if (isOldSchema(conn, 3004001)) { - try { - conn.createStatement().execute("ALTER TABLE asset ADD COLUMN asset_profile_id uuid"); - } catch (Exception e) { - } - - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.1", "schema_update_before.sql"); - loadSql(schemaUpdateFile, conn); - - conn.createStatement().execute("DELETE FROM asset a WHERE NOT exists(SELECT id FROM tenant WHERE id = a.tenant_id);"); - - log.info("Creating default asset profiles..."); - - PageLink pageLink = new PageLink(1000); - PageData tenantIds; - do { - List> futures = new ArrayList<>(); - tenantIds = tenantService.findTenantsIds(pageLink); - for (TenantId tenantId : tenantIds.getData()) { - futures.add(dbUpgradeExecutor.submit(() -> { - try { - assetProfileService.createDefaultAssetProfile(tenantId); - } catch (Exception e) { - } - })); - } - Futures.allAsList(futures).get(); - pageLink = pageLink.nextPageLink(); - } while (tenantIds.hasNext()); - - pageLink = new PageLink(1000); - PageData> pairs; - do { - List> futures = new ArrayList<>(); - pairs = assetDao.getAllAssetTypes(pageLink); - for (TbPair pair : pairs.getData()) { - TenantId tenantId = new TenantId(pair.getFirst()); - String assetType = pair.getSecond(); - if (!"default".equals(assetType)) { - futures.add(dbUpgradeExecutor.submit(() -> { - try { - assetProfileService.findOrCreateAssetProfile(tenantId, assetType); - } catch (Exception e) { - } - })); - } - } - Futures.allAsList(futures).get(); - pageLink = pageLink.nextPageLink(); - } while (pairs.hasNext()); - - log.info("Updating asset profiles..."); - conn.createStatement().execute("call update_asset_profiles()"); - - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.1", "schema_update_after.sql"); - loadSql(schemaUpdateFile, conn); - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004002;"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; - case "3.4.4": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3004002)) { - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.4.4", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - - try { - conn.createStatement().execute("VACUUM FULL ANALYZE alarm;"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - - try { - conn.createStatement().execute("ALTER TABLE asset_profile ADD COLUMN default_edge_rule_chain_id uuid"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - try { - conn.createStatement().execute("ALTER TABLE device_profile ADD COLUMN default_edge_rule_chain_id uuid"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - try { - conn.createStatement().execute("ALTER TABLE asset_profile ADD CONSTRAINT fk_default_edge_rule_chain_asset_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - try { - conn.createStatement().execute("ALTER TABLE device_profile ADD CONSTRAINT fk_default_edge_rule_chain_device_profile FOREIGN KEY (default_edge_rule_chain_id) REFERENCES rule_chain(id)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script - } catch (Exception e) { - } - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005000;"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } - break; case "3.5.0": updateSchema("3.5.0", 3005000, "3.5.1", 3005001, null); break; @@ -726,7 +66,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService "asset_profile", "asset", "device_profile", "tb_user", "tenant_profile", "tenant", "widgets_bundle", "entity_view", "edge"}; for (String entityName : entityNames) { try { - conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN " + SEARCH_TEXT + " CASCADE"); + conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN search_text CASCADE"); } catch (Exception e) { } } @@ -799,11 +139,6 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } } - private void runSchemaUpdateScript(Connection connection, String version) throws Exception { - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, connection); - } - private void loadSql(Path sqlFile, Connection conn) throws Exception { String sql = new String(Files.readAllBytes(sqlFile), Charset.forName("UTF-8")); Statement st = conn.createStatement(); @@ -848,28 +183,4 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } return isOldSchema; } - - private Queue queueConfigToQueue(TbRuleEngineQueueConfiguration queueSettings) { - Queue queue = new Queue(); - queue.setTenantId(TenantId.SYS_TENANT_ID); - queue.setName(queueSettings.getName()); - queue.setTopic(queueSettings.getTopic()); - queue.setPollInterval(queueSettings.getPollInterval()); - queue.setPartitions(queueSettings.getPartitions()); - queue.setPackProcessingTimeout(queueSettings.getPackProcessingTimeout()); - SubmitStrategy submitStrategy = new SubmitStrategy(); - submitStrategy.setBatchSize(queueSettings.getSubmitStrategy().getBatchSize()); - submitStrategy.setType(SubmitStrategyType.valueOf(queueSettings.getSubmitStrategy().getType())); - queue.setSubmitStrategy(submitStrategy); - ProcessingStrategy processingStrategy = new ProcessingStrategy(); - processingStrategy.setType(ProcessingStrategyType.valueOf(queueSettings.getProcessingStrategy().getType())); - processingStrategy.setRetries(queueSettings.getProcessingStrategy().getRetries()); - processingStrategy.setFailurePercentage(queueSettings.getProcessingStrategy().getFailurePercentage()); - processingStrategy.setPauseBetweenRetries(queueSettings.getProcessingStrategy().getPauseBetweenRetries()); - processingStrategy.setMaxPauseBetweenRetries(queueSettings.getProcessingStrategy().getMaxPauseBetweenRetries()); - queue.setProcessingStrategy(processingStrategy); - queue.setConsumerPerPartition(queueSettings.isConsumerPerPartition()); - return queue; - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java index 2de90fb9b0..ebe4cb8ef5 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseUpgradeService.java @@ -16,20 +16,13 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.SystemUtils; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.util.SqlTsDao; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; -import java.sql.DriverManager; @Service @Profile("install") @@ -37,216 +30,14 @@ import java.sql.DriverManager; @SqlTsDao public class SqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService { - @Value("${sql.postgres.ts_key_value_partitioning:MONTHS}") - private String partitionType; - - private static final String TS_KV_LATEST_SQL = "ts_kv_latest.sql"; - private static final String LOAD_FUNCTIONS_SQL = "schema_update_psql_ts.sql"; - private static final String LOAD_TTL_FUNCTIONS_SQL = "schema_update_ttl.sql"; - private static final String LOAD_DROP_PARTITIONS_FUNCTIONS_SQL = "schema_update_psql_drop_partitions.sql"; - - private static final String TS_KV_OLD = "ts_kv_old;"; - private static final String TS_KV_LATEST_OLD = "ts_kv_latest_old;"; - - private static final String CREATE_PARTITION_TS_KV_TABLE = "create_partition_ts_kv_table()"; - private static final String CREATE_NEW_TS_KV_LATEST_TABLE = "create_new_ts_kv_latest_table()"; - private static final String CREATE_PARTITIONS = "create_partitions(IN partition_type varchar)"; - private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()"; - private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()"; - private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)"; - private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest(IN path_to_file varchar)"; - private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()"; - private static final String INSERT_INTO_TS_KV_LATEST_CURSOR = "insert_into_ts_kv_latest_cursor()"; - - private static final String CALL_CREATE_PARTITION_TS_KV_TABLE = CALL_REGEX + CREATE_PARTITION_TS_KV_TABLE; - private static final String CALL_CREATE_NEW_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_LATEST_TABLE; - private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE; - private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY; - private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR; - private static final String CALL_INSERT_INTO_TS_KV_LATEST_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_LATEST_CURSOR; - - private static final String DROP_TABLE_TS_KV_OLD = DROP_TABLE + TS_KV_OLD; - private static final String DROP_TABLE_TS_KV_LATEST_OLD = DROP_TABLE + TS_KV_LATEST_OLD; - - private static final String DROP_PROCEDURE_CREATE_PARTITION_TS_KV_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_PARTITION_TS_KV_TABLE; - private static final String DROP_PROCEDURE_CREATE_NEW_TS_KV_LATEST_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_NEW_TS_KV_LATEST_TABLE; - private static final String DROP_PROCEDURE_CREATE_PARTITIONS = DROP_PROCEDURE_IF_EXISTS + CREATE_PARTITIONS; - private static final String DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_TS_KV_DICTIONARY_TABLE; - private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST_CURSOR; - private static final String DROP_FUNCTION_GET_PARTITION_DATA = "DROP FUNCTION IF EXISTS get_partitions_data;"; - @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { - case "2.4.3": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Check the current PostgreSQL version..."); - boolean versionValid = checkVersion(conn); - if (!versionValid) { - throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!"); - } else { - log.info("PostgreSQL version is valid!"); - if (isOldSchema(conn, 2004003)) { - log.info("Load upgrade functions ..."); - loadSql(conn, LOAD_FUNCTIONS_SQL, "2.4.3"); - log.info("Updating timeseries schema ..."); - executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE); - if (!partitionType.equals("INDEFINITE")) { - executeQuery(conn, "call create_partitions('" + partitionType + "')"); - } - executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE); - executeQuery(conn, CALL_INSERT_INTO_DICTIONARY); - - Path pathToTempTsKvFile = null; - Path pathToTempTsKvLatestFile = null; - if (SystemUtils.IS_OS_WINDOWS) { - log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR); - Path pathToDir; - String thingsboardWindowsUpgradeDir = System.getenv("THINGSBOARD_WINDOWS_UPGRADE_DIR"); - if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) { - log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR); - pathToDir = Paths.get(thingsboardWindowsUpgradeDir); - } else { - log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR); - pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER); - } - log.info("Directory: {} will be used for creation temporary upgrade files!", pathToDir); - try { - Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql"); - Path tsKvLatestFile = Files.createTempFile(pathToDir, "ts_kv_latest", ".sql"); - pathToTempTsKvFile = tsKvFile.toAbsolutePath(); - pathToTempTsKvLatestFile = tsKvLatestFile.toAbsolutePath(); - try { - copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile); - } catch (Exception e) { - insertTimeseries(conn); - } - } catch (IOException | SecurityException e) { - log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage()); - insertTimeseries(conn); - } - } else { - try { - Path tempDirPath = Files.createTempDirectory("ts_kv"); - File tempDirAsFile = tempDirPath.toFile(); - boolean writable = tempDirAsFile.setWritable(true, false); - boolean readable = tempDirAsFile.setReadable(true, false); - boolean executable = tempDirAsFile.setExecutable(true, false); - pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath(); - pathToTempTsKvLatestFile = tempDirPath.resolve(TS_KV_LATEST_SQL).toAbsolutePath(); - try { - if (writable && readable && executable) { - copyTimeseries(conn, pathToTempTsKvFile, pathToTempTsKvLatestFile); - } else { - throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!"); - } - } catch (Exception e) { - insertTimeseries(conn); - } - } catch (IOException | SecurityException e) { - log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage()); - insertTimeseries(conn); - } - } - - removeUpgradeFiles(pathToTempTsKvFile, pathToTempTsKvLatestFile); - - executeQuery(conn, DROP_TABLE_TS_KV_OLD); - executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD); - - executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITION_TS_KV_TABLE); - executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITIONS); - executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_DICTIONARY); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV); - executeQuery(conn, DROP_PROCEDURE_CREATE_NEW_TS_KV_LATEST_TABLE); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST_CURSOR); - executeQuery(conn, DROP_FUNCTION_GET_PARTITION_DATA); - - executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;"); - executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN IF NOT EXISTS json_v json;"); - } else { - executeQuery(conn, "ALTER TABLE ts_kv DROP CONSTRAINT IF EXISTS ts_kv_pkey;"); - executeQuery(conn, "ALTER TABLE ts_kv ADD CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts);"); - } - - log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL, "2.4.3"); - log.info("Load Drop Partitions functions ..."); - loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL, "2.4.3"); - - executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000"); - - log.info("schema timeseries updated!"); - } - } - break; - case "2.5.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - executeQuery(conn, "CREATE TABLE IF NOT EXISTS ts_kv_indefinite PARTITION OF ts_kv DEFAULT;"); - executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); - } - break; - case "3.1.1": - case "3.2.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL, "2.4.3"); - log.info("Load Drop Partitions functions ..."); - loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL, "2.4.3"); - - executeQuery(conn, "DROP PROCEDURE IF EXISTS cleanup_timeseries_by_ttl(character varying, bigint, bigint);"); - executeQuery(conn, "DROP FUNCTION IF EXISTS delete_asset_records_from_ts_kv(character varying, character varying, bigint);"); - executeQuery(conn, "DROP FUNCTION IF EXISTS delete_device_records_from_ts_kv(character varying, character varying, bigint);"); - executeQuery(conn, "DROP FUNCTION IF EXISTS delete_customer_records_from_ts_kv(character varying, character varying, bigint);"); - } - break; - case "3.2.2": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Load Drop Partitions functions ..."); - loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL, "2.4.3"); - } - break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } } - private void removeUpgradeFiles(Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) { - if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) { - boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete(); - if (deleteTsKvFile) { - log.info("Successfully deleted the temp file for ts_kv table upgrade!"); - } - } - if (pathToTempTsKvLatestFile != null && pathToTempTsKvLatestFile.toFile().exists()) { - boolean deleteTsKvLatestFile = pathToTempTsKvLatestFile.toFile().delete(); - if (deleteTsKvLatestFile) { - log.info("Successfully deleted the temp file for ts_kv_latest table upgrade!"); - } - } - } - - private void copyTimeseries(Connection conn, Path pathToTempTsKvFile, Path pathToTempTsKvLatestFile) { - executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')"); - executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE); - executeQuery(conn, "call insert_into_ts_kv_latest('" + pathToTempTsKvLatestFile + "')"); - } - - private void insertTimeseries(Connection conn) { - log.warn("Upgrade script failed using the copy to/from files strategy!" + - " Trying to perfrom the upgrade using Inserts strategy ..."); - executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR); - executeQuery(conn, CALL_CREATE_NEW_TS_KV_LATEST_TABLE); - executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST_CURSOR); - } - @Override protected void loadSql(Connection conn, String fileName, String version) { Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index 1f29665884..eeac1b6aa4 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -25,8 +25,6 @@ public interface SystemDataLoaderService { void createRandomJwtSettings() throws Exception; - void saveLegacyYmlSettings() throws Exception; - void createOAuth2Templates() throws Exception; void loadSystemWidgets() throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java b/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java deleted file mode 100644 index 890912a4b5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/TbRuleEngineQueueConfigService.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; - -import javax.annotation.PostConstruct; -import java.util.List; - -@Slf4j -@Data -@EnableAutoConfiguration -@Configuration -@ConfigurationProperties(prefix = "queue.rule-engine") -@Profile("install") -public class TbRuleEngineQueueConfigService { - - private String topic; - private List queues; - - @PostConstruct - public void validate() { - queues.stream().filter(queue -> queue.getName().equals(DataConstants.MAIN_QUEUE_NAME)).findFirst().orElseThrow(() -> { - log.error("Main queue is not configured in thingsboard.yml"); - return new RuntimeException("No \"Main\" queue configured!"); - }); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java index 3eda90c554..43b0b37d71 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java @@ -16,21 +16,14 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.SystemUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.util.TimescaleDBTsDao; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; -import java.sql.DriverManager; @Service @Profile("install") @@ -38,172 +31,17 @@ import java.sql.DriverManager; @TimescaleDBTsDao public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService { - @Value("${sql.timescale.chunk_time_interval:86400000}") - private long chunkTimeInterval; - - private static final String LOAD_FUNCTIONS_SQL = "schema_update_timescale_ts.sql"; - private static final String LOAD_TTL_FUNCTIONS_SQL = "schema_update_ttl.sql"; - - private static final String TENANT_TS_KV_OLD_TABLE = "tenant_ts_kv_old;"; - - private static final String CREATE_TS_KV_LATEST_TABLE = "create_ts_kv_latest_table()"; - private static final String CREATE_NEW_TS_KV_TABLE = "create_new_ts_kv_table()"; - private static final String CREATE_TS_KV_DICTIONARY_TABLE = "create_ts_kv_dictionary_table()"; - private static final String INSERT_INTO_DICTIONARY = "insert_into_dictionary()"; - private static final String INSERT_INTO_TS_KV = "insert_into_ts_kv(IN path_to_file varchar)"; - private static final String INSERT_INTO_TS_KV_CURSOR = "insert_into_ts_kv_cursor()"; - private static final String INSERT_INTO_TS_KV_LATEST = "insert_into_ts_kv_latest()"; - - private static final String CALL_CREATE_TS_KV_LATEST_TABLE = CALL_REGEX + CREATE_TS_KV_LATEST_TABLE; - private static final String CALL_CREATE_NEW_TENANT_TS_KV_TABLE = CALL_REGEX + CREATE_NEW_TS_KV_TABLE; - private static final String CALL_CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + CREATE_TS_KV_DICTIONARY_TABLE; - private static final String CALL_INSERT_INTO_DICTIONARY = CALL_REGEX + INSERT_INTO_DICTIONARY; - private static final String CALL_INSERT_INTO_TS_KV_LATEST = CALL_REGEX + INSERT_INTO_TS_KV_LATEST; - private static final String CALL_INSERT_INTO_TS_KV_CURSOR = CALL_REGEX + INSERT_INTO_TS_KV_CURSOR; - - private static final String DROP_OLD_TENANT_TS_KV_TABLE = DROP_TABLE + TENANT_TS_KV_OLD_TABLE; - - private static final String DROP_PROCEDURE_CREATE_TS_KV_LATEST_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_TS_KV_LATEST_TABLE; - private static final String DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY = DROP_PROCEDURE_IF_EXISTS + CREATE_NEW_TS_KV_TABLE; - private static final String DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE = DROP_PROCEDURE_IF_EXISTS + CREATE_TS_KV_DICTIONARY_TABLE; - private static final String DROP_PROCEDURE_INSERT_INTO_DICTIONARY = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_DICTIONARY; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_CURSOR; - private static final String DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST = DROP_PROCEDURE_IF_EXISTS + INSERT_INTO_TS_KV_LATEST; - @Autowired private InstallScripts installScripts; @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { - case "2.4.3": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Check the current PostgreSQL version..."); - boolean versionValid = checkVersion(conn); - if (!versionValid) { - throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!"); - } else { - log.info("PostgreSQL version is valid!"); - if (isOldSchema(conn, 2004003)) { - log.info("Load upgrade functions ..."); - loadSql(conn, LOAD_FUNCTIONS_SQL, "2.4.3"); - log.info("Updating timescale schema ..."); - executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE); - executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE); - - executeQuery(conn, "SELECT create_hypertable('ts_kv', 'ts', chunk_time_interval => " + chunkTimeInterval + ", if_not_exists => true);"); - - executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE); - executeQuery(conn, CALL_INSERT_INTO_DICTIONARY); - - Path pathToTempTsKvFile = null; - if (SystemUtils.IS_OS_WINDOWS) { - Path pathToDir; - log.info("Lookup for environment variable: {} ...", THINGSBOARD_WINDOWS_UPGRADE_DIR); - String thingsboardWindowsUpgradeDir = System.getenv(THINGSBOARD_WINDOWS_UPGRADE_DIR); - if (StringUtils.isNotEmpty(thingsboardWindowsUpgradeDir)) { - log.info("Environment variable: {} was found!", THINGSBOARD_WINDOWS_UPGRADE_DIR); - pathToDir = Paths.get(thingsboardWindowsUpgradeDir); - } else { - log.info("Failed to lookup environment variable: {}", THINGSBOARD_WINDOWS_UPGRADE_DIR); - pathToDir = Paths.get(PATH_TO_USERS_PUBLIC_FOLDER); - } - log.info("Directory: {} will be used for creation temporary upgrade file!", pathToDir); - try { - Path tsKvFile = Files.createTempFile(pathToDir, "ts_kv", ".sql"); - pathToTempTsKvFile = tsKvFile.toAbsolutePath(); - try { - executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')"); - } catch (Exception e) { - insertTimeseries(conn); - } - } catch (IOException | SecurityException e) { - log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage()); - insertTimeseries(conn); - } - } else { - try { - Path tempDirPath = Files.createTempDirectory("ts_kv"); - File tempDirAsFile = tempDirPath.toFile(); - boolean writable = tempDirAsFile.setWritable(true, false); - boolean readable = tempDirAsFile.setReadable(true, false); - boolean executable = tempDirAsFile.setExecutable(true, false); - pathToTempTsKvFile = tempDirPath.resolve(TS_KV_SQL).toAbsolutePath(); - try { - if (writable && readable && executable) { - executeQuery(conn, "call insert_into_ts_kv('" + pathToTempTsKvFile + "')"); - } else { - throw new RuntimeException("Failed to grant write permissions for the: " + tempDirPath + "folder!"); - } - } catch (Exception e) { - insertTimeseries(conn); - } - } catch (IOException | SecurityException e) { - log.warn("Failed to create time-series upgrade files due to: {}", e.getMessage()); - insertTimeseries(conn); - } - } - removeUpgradeFile(pathToTempTsKvFile); - - executeQuery(conn, CALL_INSERT_INTO_TS_KV_LATEST); - - executeQuery(conn, DROP_OLD_TENANT_TS_KV_TABLE); - - executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_LATEST_TABLE); - executeQuery(conn, DROP_PROCEDURE_CREATE_TENANT_TS_KV_TABLE_COPY); - executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_DICTIONARY); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_CURSOR); - executeQuery(conn, DROP_PROCEDURE_INSERT_INTO_TS_KV_LATEST); - - executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN IF NOT EXISTS json_v json;"); - executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN IF NOT EXISTS json_v json;"); - } - - log.info("Load TTL functions ..."); - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL, "2.4.3"); - - executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000"); - log.info("schema timescale updated!"); - } - } - break; - case "2.5.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); - } - break; - case "3.1.1": - break; - case "3.2.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - loadSql(conn, LOAD_TTL_FUNCTIONS_SQL, "3.2.1"); - } - break; - case "3.2.2": - break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } } - private void insertTimeseries(Connection conn) { - log.warn("Upgrade script failed using the copy to/from files strategy!" + - " Trying to perfrom the upgrade using Inserts strategy ..."); - executeQuery(conn, CALL_INSERT_INTO_TS_KV_CURSOR); - } - - private void removeUpgradeFile(Path pathToTempTsKvFile) { - if (pathToTempTsKvFile != null && pathToTempTsKvFile.toFile().exists()) { - boolean deleteTsKvFile = pathToTempTsKvFile.toFile().delete(); - if (deleteTsKvFile) { - log.info("Successfully deleted the temp file for ts_kv table upgrade!"); - } - } - } - @Override protected void loadSql(Connection conn, String fileName, String version) { Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName); diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java deleted file mode 100644 index db5fba831c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install.cql; - -import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; -import com.datastax.oss.driver.api.core.cql.PreparedStatement; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.Row; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata; -import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata; -import com.datastax.oss.driver.api.core.type.DataType; -import com.datastax.oss.protocol.internal.ProtocolConstants; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVPrinter; -import org.apache.commons.csv.CSVRecord; -import org.thingsboard.server.dao.cassandra.guava.GuavaSession; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.UUID; - -import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; - -public class CassandraDbHelper { - - public static Path dumpCfIfExists(KeyspaceMetadata ks, GuavaSession session, String cfName, - String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { - return dumpCfIfExists(ks, session, cfName, columns, defaultValues, dumpPrefix, false); - } - - public static Path dumpCfIfExists(KeyspaceMetadata ks, GuavaSession session, String cfName, - String[] columns, String[] defaultValues, String dumpPrefix, boolean printHeader) throws Exception { - if (ks.getTable(cfName) != null) { - Path dumpFile = Files.createTempFile(dumpPrefix, null); - Files.deleteIfExists(dumpFile); - CSVFormat csvFormat = CSV_DUMP_FORMAT; - if (printHeader) { - csvFormat = csvFormat.withHeader(columns); - } - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), csvFormat)) { - Statement stmt = SimpleStatement.newInstance("SELECT * FROM " + cfName); - stmt.setPageSize(1000); - ResultSet rs = session.execute(stmt); - Iterator iter = rs.iterator(); - while (iter.hasNext()) { - Row row = iter.next(); - if (row != null) { - dumpRow(row, columns, defaultValues, csvPrinter); - } - } - } - return dumpFile; - } else { - return null; - } - } - - public static void appendToEndOfLine(Path targetDumpFile, String toAppend) throws Exception { - Path tmp = Files.createTempFile(null, null); - try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(targetDumpFile), CSV_DUMP_FORMAT)) { - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(tmp), CSV_DUMP_FORMAT)) { - csvParser.forEach(record -> { - List newRecord = new ArrayList<>(); - record.forEach(val -> newRecord.add(val)); - newRecord.add(toAppend); - try { - csvPrinter.printRecord(newRecord); - } catch (IOException e) { - throw new RuntimeException("Error appending to EOL", e); - } - }); - } - } - Files.move(tmp, targetDumpFile, StandardCopyOption.REPLACE_EXISTING); - } - - public static void loadCf(KeyspaceMetadata ks, GuavaSession session, String cfName, String[] columns, Path sourceFile) throws Exception { - loadCf(ks, session, cfName, columns, sourceFile, false); - } - - public static void loadCf(KeyspaceMetadata ks, GuavaSession session, String cfName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception { - TableMetadata tableMetadata = ks.getTable(cfName).get(); - PreparedStatement prepared = session.prepare(createInsertStatement(cfName, columns)); - CSVFormat csvFormat = CSV_DUMP_FORMAT; - if (parseHeader) { - csvFormat = csvFormat.withFirstRecordAsHeader(); - } else { - csvFormat = CSV_DUMP_FORMAT.withHeader(columns); - } - try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), csvFormat)) { - csvParser.forEach(record -> { - BoundStatementBuilder boundStatementBuilder = new BoundStatementBuilder(prepared.bind()); - for (String column : columns) { - setColumnValue(tableMetadata, column, record, boundStatementBuilder); - } - session.execute(boundStatementBuilder.build()); - }); - } - } - - - private static void dumpRow(Row row, String[] columns, String[] defaultValues, CSVPrinter csvPrinter) throws Exception { - List record = new ArrayList<>(); - for (int i=0;i -1) { - String str; - DataType type = row.getColumnDefinitions().get(index).getType(); - try { - if (row.isNull(index)) { - return null; - } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) { - str = Double.valueOf(row.getDouble(index)).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) { - str = Integer.valueOf(row.getInt(index)).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) { - str = Long.valueOf(row.getLong(index)).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) { - str = row.getUuid(index).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) { - str = row.getUuid(index).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) { - str = Float.valueOf(row.getFloat(index)).toString(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { - str = ""+row.getInstant(index).toEpochMilli(); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { - str = Boolean.valueOf(row.getBoolean(index)).toString(); - } else { - str = row.getString(index); - } - } catch (Exception e) { - str = ""; - } - return str; - } else { - return defaultValue; - } - } - - private static String createInsertStatement(String cfName, String[] columns) { - StringBuilder insertStatementBuilder = new StringBuilder(); - insertStatementBuilder.append("INSERT INTO ").append(cfName).append(" ("); - for (String column : columns) { - insertStatementBuilder.append(column).append(","); - } - insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); - insertStatementBuilder.append(") VALUES ("); - for (String column : columns) { - insertStatementBuilder.append("?").append(","); - } - insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); - insertStatementBuilder.append(")"); - return insertStatementBuilder.toString(); - } - - private static void setColumnValue(TableMetadata tableMetadata, String column, - CSVRecord record, BoundStatementBuilder boundStatementBuilder) { - String value = record.get(column); - DataType type = tableMetadata.getColumn(column).get().getType(); - if (value == null) { - boundStatementBuilder.setToNull(column); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) { - boundStatementBuilder.setDouble(column, Double.valueOf(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) { - boundStatementBuilder.setInt(column, Integer.valueOf(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) { - boundStatementBuilder.setLong(column, Long.valueOf(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) { - boundStatementBuilder.setUuid(column, UUID.fromString(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) { - boundStatementBuilder.setUuid(column, UUID.fromString(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) { - boundStatementBuilder.setFloat(column, Float.valueOf(value)); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) { - boundStatementBuilder.setInstant(column, Instant.ofEpochMilli(Long.valueOf(value))); - } else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) { - boundStatementBuilder.setBoolean(column, Boolean.valueOf(value)); - } else { - boundStatementBuilder.setString(column, value); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraEntitiesToSqlMigrateService.java b/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraEntitiesToSqlMigrateService.java deleted file mode 100644 index 7b6944ebdd..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraEntitiesToSqlMigrateService.java +++ /dev/null @@ -1,327 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install.migrate; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.UUIDConverter; -import org.thingsboard.server.dao.cassandra.CassandraCluster; -import org.thingsboard.server.dao.util.NoSqlAnyDao; -import org.thingsboard.server.service.install.EntityDatabaseSchemaService; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.util.Arrays; -import java.util.List; - -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.bigintColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.booleanColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.doubleColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.enumToIntColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.idColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.jsonColumn; -import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.stringColumn; - -@Service -@Profile("install") -@NoSqlAnyDao -@Slf4j -public class CassandraEntitiesToSqlMigrateService implements EntitiesMigrateService { - - @Autowired - private EntityDatabaseSchemaService entityDatabaseSchemaService; - - @Autowired - protected CassandraCluster cluster; - - @Value("${spring.datasource.url}") - protected String dbUrl; - - @Value("${spring.datasource.username}") - protected String dbUserName; - - @Value("${spring.datasource.password}") - protected String dbPassword; - - @Override - public void migrate() throws Exception { - log.info("Performing migration of entities data from cassandra to SQL database ..."); - entityDatabaseSchemaService.createDatabaseSchema(false); - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - conn.setAutoCommit(false); - for (CassandraToSqlTable table: tables) { - table.migrateToSql(cluster.getSession(), conn); - } - } catch (Exception e) { - log.error("Unexpected error during ThingsBoard entities data migration!", e); - throw e; - } - entityDatabaseSchemaService.createDatabaseIndexes(); - } - - private static List tables = Arrays.asList( - new CassandraToSqlTable("admin_settings", - idColumn("id"), - stringColumn("key"), - stringColumn("json_value")), - new CassandraToSqlTable("alarm", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("type"), - idColumn("originator_id"), - enumToIntColumn("originator_type", EntityType.class), - stringColumn("severity"), - stringColumn("status"), - bigintColumn("start_ts"), - bigintColumn("end_ts"), - bigintColumn("ack_ts"), - bigintColumn("clear_ts"), - stringColumn("details", "additional_info"), - booleanColumn("propagate"), - stringColumn("propagate_relation_types")), - new CassandraToSqlTable("asset", - idColumn("id"), - idColumn("tenant_id"), - idColumn("customer_id"), - stringColumn("name"), - stringColumn("type"), - stringColumn("label"), - stringColumn("search_text"), - stringColumn("additional_info")) { - @Override - protected boolean onConstraintViolation(List batchData, - CassandraToSqlColumnData[] data, String constraint) { - if (constraint.equalsIgnoreCase("asset_name_unq_key")) { - this.handleUniqueNameViolation(data, "asset"); - return true; - } - return super.onConstraintViolation(batchData, data, constraint); - } - }, - new CassandraToSqlTable("audit_log_by_tenant_id", "audit_log", - idColumn("id"), - idColumn("tenant_id"), - idColumn("customer_id"), - idColumn("entity_id"), - stringColumn("entity_type"), - stringColumn("entity_name"), - idColumn("user_id"), - stringColumn("user_name"), - stringColumn("action_type"), - stringColumn("action_data"), - stringColumn("action_status"), - stringColumn("action_failure_details")), - new CassandraToSqlTable("attributes_kv_cf", "attribute_kv", - idColumn("entity_id"), - stringColumn("entity_type"), - stringColumn("attribute_type"), - stringColumn("attribute_key"), - booleanColumn("bool_v", true), - stringColumn("str_v"), - bigintColumn("long_v"), - doubleColumn("dbl_v"), - jsonColumn("json_v"), - bigintColumn("last_update_ts")), - new CassandraToSqlTable("component_descriptor", - idColumn("id"), - stringColumn("type"), - stringColumn("scope"), - stringColumn("name"), - stringColumn("search_text"), - stringColumn("clazz"), - stringColumn("configuration_descriptor"), - stringColumn("actions")) { - @Override - protected boolean onConstraintViolation(List batchData, - CassandraToSqlColumnData[] data, String constraint) { - if (constraint.equalsIgnoreCase("component_descriptor_clazz_key")) { - String clazz = this.getColumnData(data, "clazz").getValue(); - log.warn("Found component_descriptor record with duplicate clazz [{}]. Record will be ignored!", clazz); - this.ignoreRecord(batchData, data); - return true; - } - return super.onConstraintViolation(batchData, data, constraint); - } - }, - new CassandraToSqlTable("customer", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("title"), - stringColumn("search_text"), - stringColumn("country"), - stringColumn("state"), - stringColumn("city"), - stringColumn("address"), - stringColumn("address2"), - stringColumn("zip"), - stringColumn("phone"), - stringColumn("email"), - stringColumn("additional_info")), - new CassandraToSqlTable("dashboard", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("title"), - stringColumn("search_text"), - stringColumn("assigned_customers"), - stringColumn("configuration")), - new CassandraToSqlTable("device", - idColumn("id"), - idColumn("tenant_id"), - idColumn("customer_id"), - stringColumn("name"), - stringColumn("type"), - stringColumn("label"), - stringColumn("search_text"), - stringColumn("additional_info")) { - @Override - protected boolean onConstraintViolation(List batchData, - CassandraToSqlColumnData[] data, String constraint) { - if (constraint.equalsIgnoreCase("device_name_unq_key")) { - this.handleUniqueNameViolation(data, "device"); - return true; - } - return super.onConstraintViolation(batchData, data, constraint); - } - }, - new CassandraToSqlTable("device_credentials", - idColumn("id"), - idColumn("device_id"), - stringColumn("credentials_type"), - stringColumn("credentials_id"), - stringColumn("credentials_value")), - new CassandraToSqlTable("event", - idColumn("id"), - idColumn("tenant_id"), - idColumn("entity_id"), - stringColumn("entity_type"), - stringColumn("event_type"), - stringColumn("event_uid"), - stringColumn("body"), - new CassandraToSqlEventTsColumn()), - new CassandraToSqlTable("relation", - idColumn("from_id"), - stringColumn("from_type"), - idColumn("to_id"), - stringColumn("to_type"), - stringColumn("relation_type_group"), - stringColumn("relation_type"), - stringColumn("additional_info")), - new CassandraToSqlTable("user", "tb_user", - idColumn("id"), - idColumn("tenant_id"), - idColumn("customer_id"), - stringColumn("email"), - stringColumn("search_text"), - stringColumn("authority"), - stringColumn("first_name"), - stringColumn("last_name"), - stringColumn("additional_info")) { - @Override - protected boolean onConstraintViolation(List batchData, - CassandraToSqlColumnData[] data, String constraint) { - if (constraint.equalsIgnoreCase("tb_user_email_key")) { - this.handleUniqueEmailViolation(data); - return true; - } - return super.onConstraintViolation(batchData, data, constraint); - } - }, - new CassandraToSqlTable("tenant", - idColumn("id"), - stringColumn("title"), - stringColumn("search_text"), - stringColumn("region"), - stringColumn("country"), - stringColumn("state"), - stringColumn("city"), - stringColumn("address"), - stringColumn("address2"), - stringColumn("zip"), - stringColumn("phone"), - stringColumn("email"), - stringColumn("additional_info"), - booleanColumn("isolated_tb_core"), - booleanColumn("isolated_tb_rule_engine")), - new CassandraToSqlTable("user_credentials", - idColumn("id"), - idColumn("user_id"), - booleanColumn("enabled"), - stringColumn("password"), - stringColumn("activate_token"), - stringColumn("reset_token")) { - @Override - protected boolean onConstraintViolation(List batchData, - CassandraToSqlColumnData[] data, String constraint) { - if (constraint.equalsIgnoreCase("user_credentials_user_id_key")) { - String id = UUIDConverter.fromString(this.getColumnData(data, "id").getValue()).toString(); - log.warn("Found user credentials record with duplicate user_id [id:[{}]]. Record will be ignored!", id); - this.ignoreRecord(batchData, data); - return true; - } - return super.onConstraintViolation(batchData, data, constraint); - } - }, - new CassandraToSqlTable("widget_type", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("bundle_alias"), - stringColumn("alias"), - stringColumn("name"), - stringColumn("descriptor")), - new CassandraToSqlTable("widgets_bundle", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("alias"), - stringColumn("title"), - stringColumn("search_text")), - new CassandraToSqlTable("rule_chain", - idColumn("id"), - idColumn("tenant_id"), - stringColumn("name"), - stringColumn("search_text"), - idColumn("first_rule_node_id"), - booleanColumn("root"), - booleanColumn("debug_mode"), - stringColumn("configuration"), - stringColumn("additional_info")), - new CassandraToSqlTable("rule_node", - idColumn("id"), - idColumn("rule_chain_id"), - stringColumn("type"), - stringColumn("name"), - booleanColumn("debug_mode"), - stringColumn("search_text"), - stringColumn("configuration"), - stringColumn("additional_info")), - new CassandraToSqlTable("entity_view", - idColumn("id"), - idColumn("tenant_id"), - idColumn("customer_id"), - idColumn("entity_id"), - stringColumn("entity_type"), - stringColumn("name"), - stringColumn("type"), - stringColumn("keys"), - bigintColumn("start_ts"), - bigintColumn("end_ts"), - stringColumn("search_text"), - stringColumn("additional_info")) - ); -} diff --git a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java b/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java index e0bdc75ee8..3cc4001624 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java @@ -64,6 +64,8 @@ public class CassandraTsLatestToSqlMigrateService implements TsLatestMigrateServ private static final int MAX_KEY_LENGTH = 255; private static final int MAX_STR_V_LENGTH = 10000000; + private static final String SQL_DIR = "sql"; + @Autowired private InsertLatestTsRepository insertLatestTsRepository; @@ -93,7 +95,7 @@ public class CassandraTsLatestToSqlMigrateService implements TsLatestMigrateServ public void migrate() throws Exception { log.info("Performing migration of latest timeseries data from cassandra to SQL database ..."); try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.0.1", "schema_ts_latest.sql"); + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), SQL_DIR, "schema-ts-latest-psql.sql"); loadSql(schemaUpdateFile, conn); conn.setAutoCommit(false); for (CassandraToSqlTable table : tables) { diff --git a/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java deleted file mode 100644 index 4e861aa174..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install.sql; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVPrinter; -import org.apache.commons.csv.CSVRecord; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; - -/** - * Created by igor on 2/27/18. - */ -@Slf4j -public class SqlDbHelper { - - public static Path dumpTableIfExists(Connection conn, String tableName, - String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { - return dumpTableIfExists(conn, tableName, columns, defaultValues, dumpPrefix, false); - } - - public static Path dumpTableIfExists(Connection conn, String tableName, - String[] columns, String[] defaultValues, String dumpPrefix, boolean printHeader) throws Exception { - - if (tableExists(conn, tableName)) { - Path dumpFile = Files.createTempFile(dumpPrefix, null); - Files.deleteIfExists(dumpFile); - CSVFormat csvFormat = CSV_DUMP_FORMAT; - if (printHeader) { - csvFormat = csvFormat.withHeader(columns); - } - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), csvFormat)) { - try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName)) { - try (ResultSet tableRes = stmt.executeQuery()) { - ResultSetMetaData resMetaData = tableRes.getMetaData(); - Map columnIndexMap = new HashMap<>(); - for (int i = 1; i <= resMetaData.getColumnCount(); i++) { - String columnName = resMetaData.getColumnName(i); - columnIndexMap.put(columnName.toUpperCase(), i); - } - while(tableRes.next()) { - dumpRow(tableRes, columnIndexMap, columns, defaultValues, csvPrinter); - } - } - } - } - return dumpFile; - } else { - return null; - } - } - - private static boolean tableExists(Connection conn, String tableName) { - try (Statement stmt = conn.createStatement()) { - stmt.executeQuery("select * from " + tableName + " where 1=0"); - return true; - } catch (Exception e) { - return false; - } - } - - public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile) throws Exception { - loadTable(conn, tableName, columns, sourceFile, false); - } - - public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception { - CSVFormat csvFormat = CSV_DUMP_FORMAT; - if (parseHeader) { - csvFormat = csvFormat.withFirstRecordAsHeader(); - } else { - csvFormat = CSV_DUMP_FORMAT.withHeader(columns); - } - try (PreparedStatement prepared = conn.prepareStatement(createInsertStatement(tableName, columns))) { - try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), csvFormat)) { - csvParser.forEach(record -> { - try { - for (int i = 0; i < columns.length; i++) { - setColumnValue(i, columns[i], record, prepared); - } - prepared.execute(); - } catch (SQLException e) { - log.error("Unable to load table record!", e); - } - }); - } - } - } - - private static void dumpRow(ResultSet res, Map columnIndexMap, String[] columns, - String[] defaultValues, CSVPrinter csvPrinter) throws Exception { - List record = new ArrayList<>(); - for (int i=0;i columnIndexMap, ResultSet res) { - int index = columnIndexMap.containsKey(column.toUpperCase()) ? columnIndexMap.get(column.toUpperCase()) : -1; - if (index > -1) { - String str; - try { - Object obj = res.getObject(index); - if (obj == null) { - return null; - } else { - str = obj.toString(); - } - } catch (Exception e) { - str = ""; - } - return str; - } else { - return defaultValue; - } - } - - private static void setColumnValue(int index, String column, - CSVRecord record, PreparedStatement preparedStatement) throws SQLException { - String value = record.get(column); - int type = preparedStatement.getParameterMetaData().getParameterType(index + 1); - preparedStatement.setObject(index + 1, value, type); - } - - private static String createInsertStatement(String tableName, String[] columns) { - StringBuilder insertStatementBuilder = new StringBuilder(); - insertStatementBuilder.append("INSERT INTO ").append(tableName).append(" ("); - for (String column : columns) { - insertStatementBuilder.append(column).append(","); - } - insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); - insertStatementBuilder.append(") VALUES ("); - for (String column : columns) { - insertStatementBuilder.append("?").append(","); - } - insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); - insertStatementBuilder.append(")"); - return insertStatementBuilder.toString(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java index b20a3d961b..90e3d4bb04 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java @@ -48,47 +48,6 @@ public class DefaultCacheCleanupService implements CacheCleanupService { @Override public void clearCache(String fromVersion) throws Exception { switch (fromVersion) { - case "3.0.1": - log.info("Clear cache to upgrade from version 3.0.1 to 3.1.0 ..."); - clearAllCaches(); - //do not break to show explicit calls for next versions - case "3.1.1": - log.info("Clear cache to upgrade from version 3.1.1 to 3.2.0 ..."); - clearCacheByName("devices"); - clearCacheByName("deviceProfiles"); - clearCacheByName("tenantProfiles"); - case "3.2.2": - log.info("Clear cache to upgrade from version 3.2.2 to 3.3.0 ..."); - clearCacheByName("devices"); - clearCacheByName("deviceProfiles"); - clearCacheByName("tenantProfiles"); - clearCacheByName("relations"); - break; - case "3.3.2": - log.info("Clear cache to upgrade from version 3.3.2 to 3.3.3 ..."); - clearAll(); - break; - case "3.3.3": - log.info("Clear cache to upgrade from version 3.3.3 to 3.3.4 ..."); - clearAll(); - break; - case "3.3.4": - log.info("Clear cache to upgrade from version 3.3.4 to 3.4.0 ..."); - clearAll(); - break; - case "3.4.1": - log.info("Clear cache to upgrade from version 3.4.1 to 3.4.2 ..."); - clearCacheByName("assets"); - clearCacheByName("repositorySettings"); - break; - case "3.4.2": - log.info("Clearing cache to upgrade from version 3.4.2 to 3.4.3 ..."); - clearCacheByName("repositorySettings"); - break; - case "3.4.4": - log.info("Clearing cache to upgrade from version 3.4.4 to 3.5.0"); - clearAll(); - break; case "3.6.1": log.info("Clearing cache to upgrade from version 3.6.1 to 3.6.2"); clearCacheByName(SECURITY_SETTINGS_CACHE); diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index fe467df541..3f8fa76fd9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -20,85 +20,29 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; -import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; -import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; -import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; -import org.thingsboard.server.common.data.kv.ReadTsKvQuery; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.msg.TbNodeConnectionType; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.FilterPredicateValue; -import org.thingsboard.server.common.data.queue.ProcessingStrategy; -import org.thingsboard.server.common.data.queue.ProcessingStrategyType; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.data.queue.SubmitStrategy; -import org.thingsboard.server.common.data.queue.SubmitStrategyType; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleChainType; -import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.alarm.AlarmDao; -import org.thingsboard.server.dao.audit.AuditLogDao; import org.thingsboard.server.dao.device.DeviceConnectivityConfiguration; -import org.thingsboard.server.dao.edge.EdgeEventDao; -import org.thingsboard.server.dao.entity.EntityService; -import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; -import org.thingsboard.server.dao.queue.QueueService; -import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.sql.JpaExecutorService; -import org.thingsboard.server.dao.sql.device.DeviceProfileRepository; -import org.thingsboard.server.dao.tenant.TenantProfileService; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.RuleNodeClassInfo; -import org.thingsboard.server.service.install.InstallScripts; -import org.thingsboard.server.service.install.SystemDataLoaderService; import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.thingsboard.server.common.data.StringUtils.isBlank; @Service @Profile("install") @@ -108,58 +52,12 @@ public class DefaultDataUpdateService implements DataUpdateService { private static final int MAX_PENDING_SAVE_RULE_NODE_FUTURES = 256; private static final int DEFAULT_PAGE_SIZE = 1024; - @Autowired - private TenantService tenantService; - - @Autowired - private RelationService relationService; - @Autowired private RuleChainService ruleChainService; - @Autowired - private InstallScripts installScripts; - - @Autowired - private EntityViewService entityViewService; - - @Autowired - private TimeseriesService tsService; - - @Autowired - private EntityService entityService; - - @Autowired - private AlarmDao alarmDao; - - @Autowired - private DeviceProfileRepository deviceProfileRepository; - - @Autowired - private RateLimitsUpdater rateLimitsUpdater; - - @Autowired - private TenantProfileService tenantProfileService; - - @Lazy - @Autowired - private QueueService queueService; - @Autowired private ComponentDiscoveryService componentDiscoveryService; - @Autowired - private SystemDataLoaderService systemDataLoaderService; - - @Autowired - private EventService eventService; - - @Autowired - private AuditLogDao auditLogDao; - - @Autowired - private EdgeEventDao edgeEventDao; - @Autowired JpaExecutorService jpaExecutorService; @@ -172,57 +70,6 @@ public class DefaultDataUpdateService implements DataUpdateService { @Override public void updateData(String fromVersion) throws Exception { switch (fromVersion) { - case "1.4.0": - log.info("Updating data from version 1.4.0 to 2.0.0 ..."); - tenantsDefaultRuleChainUpdater.updateEntities(null); - break; - case "3.0.1": - log.info("Updating data from version 3.0.1 to 3.1.0 ..."); - tenantsEntityViewsUpdater.updateEntities(null); - break; - case "3.1.1": - log.info("Updating data from version 3.1.1 to 3.2.0 ..."); - tenantsRootRuleChainUpdater.updateEntities(null); - break; - case "3.2.2": - log.info("Updating data from version 3.2.2 to 3.3.0 ..."); - tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); - tenantsAlarmsCustomerUpdater.updateEntities(null); - deviceProfileEntityDynamicConditionsUpdater.updateEntities(null); - updateOAuth2Params(); - break; - case "3.3.2": - log.info("Updating data from version 3.3.2 to 3.3.3 ..."); - updateNestedRuleChains(); - break; - case "3.3.4": - log.info("Updating data from version 3.3.4 to 3.4.0 ..."); - tenantsProfileQueueConfigurationUpdater.updateEntities(); - rateLimitsUpdater.updateEntities(); - break; - case "3.4.0": - boolean skipEventsMigration = getEnv("TB_SKIP_EVENTS_MIGRATION", false); - if (!skipEventsMigration) { - log.info("Updating data from version 3.4.0 to 3.4.1 ..."); - eventService.migrateEvents(); - } - break; - case "3.4.1": - log.info("Updating data from version 3.4.1 to 3.4.2 ..."); - systemDataLoaderService.saveLegacyYmlSettings(); - boolean skipAuditLogsMigration = getEnv("TB_SKIP_AUDIT_LOGS_MIGRATION", false); - if (!skipAuditLogsMigration) { - log.info("Starting audit logs migration. Can be skipped with TB_SKIP_AUDIT_LOGS_MIGRATION env variable set to true"); - auditLogDao.migrateAuditLogs(); - } else { - log.info("Skipping audit logs migration"); - } - migrateEdgeEvents("Starting edge events migration. "); - break; - case "3.5.1": - log.info("Updating data from version 3.5.1 to 3.6.0 ..."); - migrateEdgeEvents("Starting edge events migration - adding seq_id column. "); - break; case "3.6.0": log.info("Updating data from version 3.6.0 to 3.6.1 ..."); migrateDeviceConnectivity(); @@ -232,16 +79,6 @@ public class DefaultDataUpdateService implements DataUpdateService { } } - private void migrateEdgeEvents(String logPrefix) { - boolean skipEdgeEventsMigration = getEnv("TB_SKIP_EDGE_EVENTS_MIGRATION", false); - if (!skipEdgeEventsMigration) { - log.info(logPrefix + "Can be skipped with TB_SKIP_EDGE_EVENTS_MIGRATION env variable set to true"); - edgeEventDao.migrateEdgeEvents(); - } else { - log.info("Skipping edge events migration"); - } - } - private void migrateDeviceConnectivity() { if (adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "connectivity") == null) { AdminSettings connectivitySettings = new AdminSettings(); @@ -320,27 +157,6 @@ public class DefaultDataUpdateService implements DataUpdateService { return ruleNodeIds; } - private final PaginatedUpdater deviceProfileEntityDynamicConditionsUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Device Profile Entity Dynamic Conditions Updater"; - } - - @Override - protected PageData findEntities(String id, PageLink pageLink) { - return DaoUtil.pageToPageData(deviceProfileRepository.findAll(DaoUtil.toPageable(pageLink))); - } - - @Override - protected void updateEntity(DeviceProfileEntity deviceProfile) { - if (convertDeviceProfileForVersion330(deviceProfile.getProfileData())) { - deviceProfileRepository.save(deviceProfile); - } - } - }; - boolean convertDeviceProfileForVersion330(JsonNode profileData) { boolean isUpdated = false; if (profileData.has("alarms") && !profileData.get("alarms").isNull()) { @@ -368,327 +184,6 @@ public class DefaultDataUpdateService implements DataUpdateService { return isUpdated; } - private final PaginatedUpdater tenantsDefaultRuleChainUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Tenants default rule chain updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String region, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - try { - RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId()); - if (ruleChain == null) { - installScripts.createDefaultRuleChains(tenant.getId()); - } - } catch (Exception e) { - log.error("Unable to update Tenant", e); - } - } - }; - - private void updateNestedRuleChains() { - try { - var updated = 0; - boolean hasNext = true; - while (hasNext) { - List relations = relationService.findRuleNodeToRuleChainRelations(TenantId.SYS_TENANT_ID, RuleChainType.CORE, DEFAULT_PAGE_SIZE); - hasNext = relations.size() == DEFAULT_PAGE_SIZE; - for (EntityRelation relation : relations) { - try { - RuleNodeId sourceNodeId = new RuleNodeId(relation.getFrom().getId()); - RuleNode sourceNode = ruleChainService.findRuleNodeById(TenantId.SYS_TENANT_ID, sourceNodeId); - if (sourceNode == null) { - log.info("Skip processing of relation for non existing source rule node: [{}]", sourceNodeId); - relationService.deleteRelation(TenantId.SYS_TENANT_ID, relation); - continue; - } - RuleChainId sourceRuleChainId = sourceNode.getRuleChainId(); - RuleChainId targetRuleChainId = new RuleChainId(relation.getTo().getId()); - RuleChain targetRuleChain = ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, targetRuleChainId); - if (targetRuleChain == null) { - log.info("Skip processing of relation for non existing target rule chain: [{}]", targetRuleChainId); - relationService.deleteRelation(TenantId.SYS_TENANT_ID, relation); - continue; - } - TenantId tenantId = targetRuleChain.getTenantId(); - RuleNode targetNode = new RuleNode(); - targetNode.setName(targetRuleChain.getName()); - targetNode.setRuleChainId(sourceRuleChainId); - targetNode.setType(TbRuleChainInputNode.class.getName()); - TbRuleChainInputNodeConfiguration configuration = new TbRuleChainInputNodeConfiguration(); - configuration.setRuleChainId(targetRuleChain.getId().toString()); - targetNode.setConfiguration(JacksonUtil.valueToTree(configuration)); - targetNode.setAdditionalInfo(relation.getAdditionalInfo()); - targetNode.setDebugMode(false); - targetNode = ruleChainService.saveRuleNode(tenantId, targetNode); - - EntityRelation sourceRuleChainToRuleNode = new EntityRelation(); - sourceRuleChainToRuleNode.setFrom(sourceRuleChainId); - sourceRuleChainToRuleNode.setTo(targetNode.getId()); - sourceRuleChainToRuleNode.setType(EntityRelation.CONTAINS_TYPE); - sourceRuleChainToRuleNode.setTypeGroup(RelationTypeGroup.RULE_CHAIN); - relationService.saveRelation(tenantId, sourceRuleChainToRuleNode); - - EntityRelation sourceRuleNodeToTargetRuleNode = new EntityRelation(); - sourceRuleNodeToTargetRuleNode.setFrom(sourceNode.getId()); - sourceRuleNodeToTargetRuleNode.setTo(targetNode.getId()); - sourceRuleNodeToTargetRuleNode.setType(relation.getType()); - sourceRuleNodeToTargetRuleNode.setTypeGroup(RelationTypeGroup.RULE_NODE); - sourceRuleNodeToTargetRuleNode.setAdditionalInfo(relation.getAdditionalInfo()); - relationService.saveRelation(tenantId, sourceRuleNodeToTargetRuleNode); - - //Delete old relation - relationService.deleteRelation(tenantId, relation); - updated++; - } catch (Exception e) { - log.info("Failed to update RuleNodeToRuleChainRelation: {}", relation, e); - } - } - if (updated > 0) { - log.info("RuleNodeToRuleChainRelations: {} entities updated so far...", updated); - } - } - } catch (Exception e) { - log.error("Unable to update Tenant", e); - } - } - - private final PaginatedUpdater tenantsDefaultEdgeRuleChainUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Tenants default edge rule chain updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String region, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - try { - RuleChain defaultEdgeRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(tenant.getId()); - if (defaultEdgeRuleChain == null) { - installScripts.createDefaultEdgeRuleChains(tenant.getId()); - } - } catch (Exception e) { - log.error("Unable to update Tenant", e); - } - } - }; - - private final PaginatedUpdater tenantsRootRuleChainUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Tenants root rule chain updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String region, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - try { - RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId()); - if (ruleChain == null) { - installScripts.createDefaultRuleChains(tenant.getId()); - } else { - RuleChainMetaData md = ruleChainService.loadRuleChainMetaData(tenant.getId(), ruleChain.getId()); - int oldIdx = md.getFirstNodeIndex(); - int newIdx = md.getNodes().size(); - - if (md.getNodes().size() < oldIdx) { - // Skip invalid rule chains - return; - } - - RuleNode oldFirstNode = md.getNodes().get(oldIdx); - if (oldFirstNode.getType().equals(TbDeviceProfileNode.class.getName())) { - // No need to update the rule node twice. - return; - } - - RuleNode ruleNode = new RuleNode(); - ruleNode.setRuleChainId(ruleChain.getId()); - ruleNode.setName("Device Profile Node"); - ruleNode.setType(TbDeviceProfileNode.class.getName()); - ruleNode.setDebugMode(false); - TbDeviceProfileNodeConfiguration ruleNodeConfiguration = new TbDeviceProfileNodeConfiguration().defaultConfiguration(); - ruleNode.setConfiguration(JacksonUtil.valueToTree(ruleNodeConfiguration)); - ObjectNode additionalInfo = JacksonUtil.newObjectNode(); - additionalInfo.put("description", "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type."); - additionalInfo.put("layoutX", 204); - additionalInfo.put("layoutY", 240); - ruleNode.setAdditionalInfo(additionalInfo); - - md.getNodes().add(ruleNode); - md.setFirstNodeIndex(newIdx); - md.addConnectionInfo(newIdx, oldIdx, TbNodeConnectionType.SUCCESS); - ruleChainService.saveRuleChainMetaData(tenant.getId(), md, Function.identity()); - } - } catch (Exception e) { - log.error("[{}] Unable to update Tenant: {}", tenant.getId(), tenant.getName(), e); - } - } - }; - - private final PaginatedUpdater tenantsEntityViewsUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Tenants entity views updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String region, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - updateTenantEntityViews(tenant.getId()); - } - }; - - private void updateTenantEntityViews(TenantId tenantId) { - PageLink pageLink = new PageLink(100); - PageData pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); - boolean hasNext = true; - while (hasNext) { - List>> updateFutures = new ArrayList<>(); - for (EntityView entityView : pageData.getData()) { - updateFutures.add(updateEntityViewLatestTelemetry(entityView)); - } - - try { - Futures.allAsList(updateFutures).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to copy latest telemetry to entity view", e); - } - - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink); - } else { - hasNext = false; - } - } - } - - private ListenableFuture> updateEntityViewLatestTelemetry(EntityView entityView) { - EntityViewId entityId = entityView.getId(); - List keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ? - entityView.getKeys().getTimeseries() : Collections.emptyList(); - long startTs = entityView.getStartTimeMs(); - long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs(); - ListenableFuture> keysFuture; - if (keys.isEmpty()) { - keysFuture = Futures.transform(tsService.findAllLatest(TenantId.SYS_TENANT_ID, - entityView.getEntityId()), latest -> latest.stream().map(TsKvEntry::getKey).collect(Collectors.toList()), MoreExecutors.directExecutor()); - } else { - keysFuture = Futures.immediateFuture(keys); - } - ListenableFuture> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> { - List queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList()); - if (!queries.isEmpty()) { - return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries); - } else { - return Futures.immediateFuture(null); - } - }, MoreExecutors.directExecutor()); - return Futures.transformAsync(latestFuture, latestValues -> { - if (latestValues != null && !latestValues.isEmpty()) { - ListenableFuture> saveFuture = tsService.saveLatest(TenantId.SYS_TENANT_ID, entityId, latestValues); - return saveFuture; - } - return Futures.immediateFuture(null); - }, MoreExecutors.directExecutor()); - } - - private final PaginatedUpdater tenantsAlarmsCustomerUpdater = - new PaginatedUpdater<>() { - - final AtomicLong processed = new AtomicLong(); - - @Override - protected String getName() { - return "Tenants alarms customer updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String region, PageLink pageLink) { - return tenantService.findTenants(pageLink); - } - - @Override - protected void updateEntity(Tenant tenant) { - updateTenantAlarmsCustomer(tenant.getId(), getName(), processed); - } - }; - - private void updateTenantAlarmsCustomer(TenantId tenantId, String name, AtomicLong processed) { - AlarmQuery alarmQuery = new AlarmQuery(null, new TimePageLink(1000), null, null, null, false); - PageData alarms = alarmDao.findAlarms(tenantId, alarmQuery); - boolean hasNext = true; - while (hasNext) { - for (Alarm alarm : alarms.getData()) { - if (alarm.getCustomerId() == null && alarm.getOriginator() != null) { - alarm.setCustomerId(entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()).get()); - alarmDao.save(tenantId, alarm); - } - if (processed.incrementAndGet() % 1000 == 0) { - log.info("{}: {} alarms processed so far...", name, processed); - } - } - if (alarms.hasNext()) { - alarmQuery.setPageLink(alarmQuery.getPageLink().nextPageLink()); - alarms = alarmDao.findAlarms(tenantId, alarmQuery); - } else { - hasNext = false; - } - } - } - boolean convertDeviceProfileAlarmRulesForVersion330(JsonNode spec) { if (spec != null) { if (spec.has("type") && spec.get("type").asText().equals("DURATION")) { @@ -716,73 +211,6 @@ public class DefaultDataUpdateService implements DataUpdateService { return false; } - private void updateOAuth2Params() { - log.warn("CAUTION: Update of Oauth2 parameters from 3.2.2 to 3.3.0 available only in ThingsBoard versions 3.3.0/3.3.1"); - } - - private final PaginatedUpdater tenantsProfileQueueConfigurationUpdater = - new PaginatedUpdater<>() { - - @Override - protected String getName() { - return "Tenant profiles queue configuration updater"; - } - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected PageData findEntities(String id, PageLink pageLink) { - return tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, pageLink); - } - - @Override - protected void updateEntity(TenantProfile tenantProfile) { - updateTenantProfileQueueConfiguration(tenantProfile); - } - }; - - private void updateTenantProfileQueueConfiguration(TenantProfile profile) { - try { - List queueConfiguration = profile.getProfileData().getQueueConfiguration(); - if (profile.isIsolatedTbRuleEngine() && (queueConfiguration == null || queueConfiguration.isEmpty())) { - TenantProfileQueueConfiguration mainQueueConfig = getMainQueueConfiguration(); - profile.getProfileData().setQueueConfiguration(Collections.singletonList((mainQueueConfig))); - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, profile); - List isolatedTenants = tenantService.findTenantIdsByTenantProfileId(profile.getId()); - isolatedTenants.forEach(tenantId -> { - queueService.saveQueue(new Queue(tenantId, mainQueueConfig)); - }); - } - } catch (Exception e) { - log.error("Failed to update tenant profile queue configuration name=[" + profile.getName() + "], id=[" + profile.getId().getId() + "]", e); - } - } - - private TenantProfileQueueConfiguration getMainQueueConfiguration() { - TenantProfileQueueConfiguration mainQueueConfiguration = new TenantProfileQueueConfiguration(); - mainQueueConfiguration.setName(DataConstants.MAIN_QUEUE_NAME); - mainQueueConfiguration.setTopic(DataConstants.MAIN_QUEUE_TOPIC); - mainQueueConfiguration.setPollInterval(25); - mainQueueConfiguration.setPartitions(10); - mainQueueConfiguration.setConsumerPerPartition(true); - mainQueueConfiguration.setPackProcessingTimeout(2000); - SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); - mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); - mainQueueSubmitStrategy.setBatchSize(1000); - mainQueueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy); - ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); - mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); - mainQueueProcessingStrategy.setRetries(3); - mainQueueProcessingStrategy.setFailurePercentage(0); - mainQueueProcessingStrategy.setPauseBetweenRetries(3); - mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); - mainQueueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy); - return mainQueueConfiguration; - } - public static boolean getEnv(String name, boolean defaultValue) { String env = System.getenv(name); if (env == null) { diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/RateLimitsUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/RateLimitsUpdater.java deleted file mode 100644 index 4a2abcc6cf..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/install/update/RateLimitsUpdater.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright © 2016-2024 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. - */ -package org.thingsboard.server.service.install.update; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.tenant.TenantProfileService; - -@Component -class RateLimitsUpdater extends PaginatedUpdater { - - @Value("#{ environment.getProperty('TB_SERVER_REST_LIMITS_TENANT_ENABLED') ?: environment.getProperty('server.rest.limits.tenant.enabled') ?: 'false' }") - boolean tenantServerRestLimitsEnabled; - @Value("#{ environment.getProperty('TB_SERVER_REST_LIMITS_TENANT_CONFIGURATION') ?: environment.getProperty('server.rest.limits.tenant.configuration') ?: '100:1,2000:60' }") - String tenantServerRestLimitsConfiguration; - @Value("#{ environment.getProperty('TB_SERVER_REST_LIMITS_CUSTOMER_ENABLED') ?: environment.getProperty('server.rest.limits.customer.enabled') ?: 'false' }") - boolean customerServerRestLimitsEnabled; - @Value("#{ environment.getProperty('TB_SERVER_REST_LIMITS_CUSTOMER_CONFIGURATION') ?: environment.getProperty('server.rest.limits.customer.configuration') ?: '50:1,1000:60' }") - String customerServerRestLimitsConfiguration; - - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_TENANT') ?: environment.getProperty('server.ws.limits.max_sessions_per_tenant') ?: '0' }") - private int maxWsSessionsPerTenant; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_CUSTOMER') ?: environment.getProperty('server.ws.limits.max_sessions_per_customer') ?: '0' }") - private int maxWsSessionsPerCustomer; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_REGULAR_USER') ?: environment.getProperty('server.ws.limits.max_sessions_per_regular_user') ?: '0' }") - private int maxWsSessionsPerRegularUser; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_PUBLIC_USER') ?: environment.getProperty('server.ws.limits.max_sessions_per_public_user') ?: '0' }") - private int maxWsSessionsPerPublicUser; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_QUEUE_PER_WS_SESSION') ?: environment.getProperty('server.ws.limits.max_queue_per_ws_session') ?: '500' }") - private int wsMsgQueueLimitPerSession; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_TENANT') ?: environment.getProperty('server.ws.limits.max_subscriptions_per_tenant') ?: '0' }") - private long maxWsSubscriptionsPerTenant; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_CUSTOMER') ?: environment.getProperty('server.ws.limits.max_subscriptions_per_customer') ?: '0' }") - private long maxWsSubscriptionsPerCustomer; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_REGULAR_USER') ?: environment.getProperty('server.ws.limits.max_subscriptions_per_regular_user') ?: '0' }") - private long maxWsSubscriptionsPerRegularUser; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_PUBLIC_USER') ?: environment.getProperty('server.ws.limits.max_subscriptions_per_public_user') ?: '0' }") - private long maxWsSubscriptionsPerPublicUser; - @Value("#{ environment.getProperty('TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_UPDATES_PER_SESSION') ?: environment.getProperty('server.ws.limits.max_updates_per_session') ?: '300:1,3000:60' }") - private String wsUpdatesPerSessionRateLimit; - - @Value("#{ environment.getProperty('CASSANDRA_QUERY_TENANT_RATE_LIMITS_ENABLED') ?: environment.getProperty('cassandra.query.tenant_rate_limits.enabled') ?: 'false' }") - private boolean cassandraQueryTenantRateLimitsEnabled; - @Value("#{ environment.getProperty('CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION') ?: environment.getProperty('cassandra.query.tenant_rate_limits.configuration') ?: '1000:1,30000:60' }") - private String cassandraQueryTenantRateLimitsConfiguration; - - @Autowired - private TenantProfileService tenantProfileService; - - @Override - protected boolean forceReportTotal() { - return true; - } - - @Override - protected String getName() { - return "Rate limits updater"; - } - - @Override - protected PageData findEntities(String id, PageLink pageLink) { - return tenantProfileService.findTenantProfiles(TenantId.SYS_TENANT_ID, pageLink); - } - - @Override - protected void updateEntity(TenantProfile tenantProfile) { - var profileConfiguration = tenantProfile.getDefaultProfileConfiguration(); - - if (tenantServerRestLimitsEnabled && StringUtils.isNotEmpty(tenantServerRestLimitsConfiguration)) { - profileConfiguration.setTenantServerRestLimitsConfiguration(tenantServerRestLimitsConfiguration); - } - if (customerServerRestLimitsEnabled && StringUtils.isNotEmpty(customerServerRestLimitsConfiguration)) { - profileConfiguration.setCustomerServerRestLimitsConfiguration(customerServerRestLimitsConfiguration); - } - - profileConfiguration.setMaxWsSessionsPerTenant(maxWsSessionsPerTenant); - profileConfiguration.setMaxWsSessionsPerCustomer(maxWsSessionsPerCustomer); - profileConfiguration.setMaxWsSessionsPerPublicUser(maxWsSessionsPerPublicUser); - profileConfiguration.setMaxWsSessionsPerRegularUser(maxWsSessionsPerRegularUser); - profileConfiguration.setMaxWsSubscriptionsPerTenant(maxWsSubscriptionsPerTenant); - profileConfiguration.setMaxWsSubscriptionsPerCustomer(maxWsSubscriptionsPerCustomer); - profileConfiguration.setMaxWsSubscriptionsPerPublicUser(maxWsSubscriptionsPerPublicUser); - profileConfiguration.setMaxWsSubscriptionsPerRegularUser(maxWsSubscriptionsPerRegularUser); - profileConfiguration.setWsMsgQueueLimitPerSession(wsMsgQueueLimitPerSession); - if (StringUtils.isNotEmpty(wsUpdatesPerSessionRateLimit)) { - profileConfiguration.setWsUpdatesPerSessionRateLimit(wsUpdatesPerSessionRateLimit); - } - - if (cassandraQueryTenantRateLimitsEnabled && StringUtils.isNotEmpty(cassandraQueryTenantRateLimitsConfiguration)) { - profileConfiguration.setCassandraQueryTenantRateLimitsConfiguration(cassandraQueryTenantRateLimitsConfiguration); - } - - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 70e8cfeffe..5a9e3fc3e1 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -62,7 +62,7 @@ import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TopicService; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index a68708d8ae..a86315ce86 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.notification.NotificationRequestService; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.notification.NotificationDeduplicationService; import org.thingsboard.server.service.executors.NotificationExecutorService; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java new file mode 100644 index 0000000000..38d8ae9805 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.notification.info.EdgeCommunicationFailureNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Service +@RequiredArgsConstructor +public class EdgeCommunicationFailureTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(EdgeCommunicationFailureTrigger trigger, EdgeCommunicationFailureNotificationRuleTriggerConfig triggerConfig) { + if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { + return !triggerConfig.getEdges().contains(trigger.getEdgeId().getId()); + } + return true; + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeCommunicationFailureTrigger trigger) { + return EdgeCommunicationFailureNotificationInfo.builder() + .tenantId(trigger.getTenantId()) + .edgeId(trigger.getEdgeId()) + .customerId(trigger.getCustomerId()) + .edgeName(trigger.getEdgeName()) + .failureMsg(truncateFailureMsg(trigger.getFailureMsg())) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; + } + + private String truncateFailureMsg(String input) { + int maxLength = 500; + if (input != null && input.length() > maxLength) { + return input.substring(0, maxLength); + } + return input; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java new file mode 100644 index 0000000000..ed1ee74622 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.notification.info.EdgeConnectionNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeConnectionTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig.EdgeConnectivityEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Service +@RequiredArgsConstructor +public class EdgeConnectionTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(EdgeConnectionTrigger trigger, EdgeConnectionNotificationRuleTriggerConfig triggerConfig) { + EdgeConnectivityEvent event = trigger.isConnected() ? EdgeConnectivityEvent.CONNECTED : EdgeConnectivityEvent.DISCONNECTED; + if (CollectionUtils.isEmpty(triggerConfig.getNotifyOn()) || !triggerConfig.getNotifyOn().contains(event)) { + return false; + } + if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { + return triggerConfig.getEdges().contains(trigger.getEdgeId().getId()); + } + return true; + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeConnectionTrigger trigger) { + return EdgeConnectionNotificationInfo.builder() + .eventType(trigger.isConnected() ? "connected" : "disconnected") + .tenantId(trigger.getTenantId()) + .customerId(trigger.getCustomerId()) + .edgeId(trigger.getEdgeId()) + .edgeName(trigger.getEdgeName()) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_CONNECTION; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index ee05e82900..75e9e0ae2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -62,6 +62,8 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; +import org.thingsboard.server.gen.transport.TransportProtos.QueueDeleteMsg; +import org.thingsboard.server.gen.transport.TransportProtos.QueueUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; @@ -81,9 +83,11 @@ import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import java.util.Optional; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import static org.thingsboard.server.common.util.ProtoUtils.toProto; @@ -563,40 +567,40 @@ public class DefaultTbClusterService implements TbClusterService { } @Override - public void onQueueChange(Queue queue) { - log.trace("[{}][{}] Processing queue change [{}] event", queue.getTenantId(), queue.getId(), queue.getName()); - - TransportProtos.QueueUpdateMsg queueUpdateMsg = TransportProtos.QueueUpdateMsg.newBuilder() - .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) - .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) - .setQueueIdMSB(queue.getId().getId().getMostSignificantBits()) - .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits()) - .setQueueName(queue.getName()) - .setQueueTopic(queue.getTopic()) - .setPartitions(queue.getPartitions()) - .build(); - - ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build(); - ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build(); - ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueUpdateMsg(queueUpdateMsg).build(); + public void onQueuesUpdate(List queues) { + List queueUpdateMsgs = queues.stream() + .map(queue -> QueueUpdateMsg.newBuilder() + .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) + .setQueueIdMSB(queue.getId().getId().getMostSignificantBits()) + .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits()) + .setQueueName(queue.getName()) + .setQueueTopic(queue.getTopic()) + .setPartitions(queue.getPartitions()) + .build()) + .collect(Collectors.toList()); + + ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().addAllQueueUpdateMsgs(queueUpdateMsgs).build(); + ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().addAllQueueUpdateMsgs(queueUpdateMsgs).build(); + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().addAllQueueUpdateMsgs(queueUpdateMsgs).build(); doSendQueueNotifications(ruleEngineMsg, coreMsg, transportMsg); } @Override - public void onQueueDelete(Queue queue) { - log.trace("[{}][{}] Processing queue delete [{}] event", queue.getTenantId(), queue.getId(), queue.getName()); - - TransportProtos.QueueDeleteMsg queueDeleteMsg = TransportProtos.QueueDeleteMsg.newBuilder() - .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) - .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) - .setQueueIdMSB(queue.getId().getId().getMostSignificantBits()) - .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits()) - .setQueueName(queue.getName()) - .build(); - - ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build(); - ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build(); - ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setQueueDeleteMsg(queueDeleteMsg).build(); + public void onQueuesDelete(List queues) { + List queueDeleteMsgs = queues.stream() + .map(queue -> QueueDeleteMsg.newBuilder() + .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) + .setQueueIdMSB(queue.getId().getId().getMostSignificantBits()) + .setQueueIdLSB(queue.getId().getId().getLeastSignificantBits()) + .setQueueName(queue.getName()) + .build()) + .collect(Collectors.toList()); + + ToRuleEngineNotificationMsg ruleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().addAllQueueDeleteMsgs(queueDeleteMsgs).build(); + ToCoreNotificationMsg coreMsg = ToCoreNotificationMsg.newBuilder().addAllQueueDeleteMsgs(queueDeleteMsgs).build(); + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().addAllQueueDeleteMsgs(queueDeleteMsgs).build(); doSendQueueNotifications(ruleEngineMsg, coreMsg, transportMsg); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 2d5e6963eb..0357d6f2df 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -391,13 +391,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService 0) { + partitionService.updateQueues(toCoreNotification.getQueueUpdateMsgsList()); callback.onSuccess(); - } else if (toCoreNotification.hasQueueDeleteMsg()) { - TransportProtos.QueueDeleteMsg queue = toCoreNotification.getQueueDeleteMsg(); - partitionService.removeQueue(queue); + } else if (toCoreNotification.getQueueDeleteMsgsCount() > 0) { + partitionService.removeQueues(toCoreNotification.getQueueDeleteMsgsList()); callback.onSuccess(); } else if (toCoreNotification.hasVcResponseMsg()) { vcQueueService.processResponse(toCoreNotification.getVcResponseMsg()); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 4ebce20b32..04f57bf54d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -36,6 +36,8 @@ import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.QueueDeleteMsg; +import org.thingsboard.server.gen.transport.TransportProtos.QueueUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; @@ -164,11 +166,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< , proto.getResponse(), error); tbDeviceRpcService.processRpcResponseFromDevice(response); callback.onSuccess(); - } else if (nfMsg.hasQueueUpdateMsg()) { - updateQueue(nfMsg.getQueueUpdateMsg()); + } else if (nfMsg.getQueueUpdateMsgsCount() > 0) { + updateQueues(nfMsg.getQueueUpdateMsgsList()); callback.onSuccess(); - } else if (nfMsg.hasQueueDeleteMsg()) { - deleteQueue(nfMsg.getQueueDeleteMsg()); + } else if (nfMsg.getQueueDeleteMsgsCount() > 0) { + deleteQueues(nfMsg.getQueueDeleteMsgsList()); callback.onSuccess(); } else { log.trace("Received notification with missing handler"); @@ -176,39 +178,48 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< } } - private void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { - log.info("Received queue update msg: [{}]", queueUpdateMsg); - TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); - if (partitionService.isManagedByCurrentService(tenantId)) { - QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); - String queueName = queueUpdateMsg.getQueueName(); - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueName, tenantId); - Queue queue = queueService.findQueueById(tenantId, queueId); - - TbRuleEngineQueueConsumerManager consumerManager = getOrCreateConsumer(queueKey); - Queue oldQueue = consumerManager.getQueue(); - consumerManager.update(queue); - - if (oldQueue != null && queue.getPartitions() == oldQueue.getPartitions()) { - return; + private void updateQueues(List queueUpdateMsgs) { + boolean partitionsChanged = false; + for (QueueUpdateMsg queueUpdateMsg : queueUpdateMsgs) { + log.info("Received queue update msg: [{}]", queueUpdateMsg); + TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); + if (partitionService.isManagedByCurrentService(tenantId)) { + QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); + String queueName = queueUpdateMsg.getQueueName(); + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueName, tenantId); + Queue queue = queueService.findQueueById(tenantId, queueId); + + TbRuleEngineQueueConsumerManager consumerManager = getOrCreateConsumer(queueKey); + Queue oldQueue = consumerManager.getQueue(); + consumerManager.update(queue); + + if (oldQueue == null || queue.getPartitions() != oldQueue.getPartitions()) { + partitionsChanged = true; + } + } else { + partitionsChanged = true; } } - partitionService.updateQueue(queueUpdateMsg); - partitionService.recalculatePartitions(ctx.getServiceInfoProvider().getServiceInfo(), - new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); + if (partitionsChanged) { + partitionService.updateQueues(queueUpdateMsgs); + partitionService.recalculatePartitions(ctx.getServiceInfoProvider().getServiceInfo(), + new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); + } } - private void deleteQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { - log.info("Received queue delete msg: [{}]", queueDeleteMsg); - TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); - var consumerManager = consumers.remove(queueKey); - if (consumerManager != null) { - consumerManager.delete(true); + private void deleteQueues(List queueDeleteMsgs) { + for (QueueDeleteMsg queueDeleteMsg : queueDeleteMsgs) { + log.info("Received queue delete msg: [{}]", queueDeleteMsg); + TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); + var consumerManager = consumers.remove(queueKey); + if (consumerManager != null) { + consumerManager.delete(true); + } } - partitionService.removeQueue(queueDeleteMsg); + partitionService.removeQueues(queueDeleteMsgs); partitionService.recalculatePartitions(ctx.getServiceInfoProvider().getServiceInfo(), new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java index 0fa15ad0b3..eba68d6a09 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java @@ -184,9 +184,9 @@ public class TbCoreConsumerStats { toCoreNfEdgeSyncResponseCounter.increment(); } else if (!msg.getFromEdgeSyncResponseMsg().isEmpty()) { toCoreNfEdgeSyncResponseCounter.increment(); - } else if (msg.hasQueueUpdateMsg()) { + } else if (msg.getQueueUpdateMsgsCount() > 0) { toCoreNfQueueUpdateCounter.increment(); - } else if (msg.hasQueueDeleteMsg()) { + } else if (msg.getQueueDeleteMsgsCount() > 0) { toCoreNfQueueDeleteCounter.increment(); } else if (msg.hasVcResponseMsg()) { toCoreNfVersionControlResponseCounter.increment(); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java index d7ddd5d81b..1f9e85bdc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleNodeInfo; import org.thingsboard.server.common.msg.queue.TbMsgCallback; -import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import java.util.UUID; import java.util.concurrent.TimeUnit; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractTbRuleEngineSubmitStrategy.java index 0205059225..1ff7ba433b 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractTbRuleEngineSubmitStrategy.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.queue.processing; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; @@ -51,7 +52,16 @@ public abstract class AbstractTbRuleEngineSubmitStrategy implements TbRuleEngine List> newOrderedMsgList = new ArrayList<>(reprocessMap.size()); for (IdMsgPair pair : orderedMsgList) { if (reprocessMap.containsKey(pair.uuid)) { - newOrderedMsgList.add(pair); + if (StringUtils.isNotEmpty(pair.getMsg().getValue().getFailureMessage())) { + var toRuleEngineMsg = TransportProtos.ToRuleEngineMsg.newBuilder(pair.getMsg().getValue()) + .clearFailureMessage() + .clearRelationTypes() + .build(); + var newMsg = new TbProtoQueueMsg<>(pair.getMsg().getKey(), toRuleEngineMsg, pair.getMsg().getHeaders()); + newOrderedMsgList.add(new IdMsgPair<>(pair.getUuid(), newMsg)); + } else { + newOrderedMsgList.add(pair); + } } } orderedMsgList = newOrderedMsgList; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java index b2ebcb0b5d..d4425372e1 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProvi import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.common.data.limit.LimitedApi; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; import org.thingsboard.server.service.security.auth.mfa.provider.TwoFaProvider; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java index 96bdbd5561..5dc6b0e636 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java @@ -19,7 +19,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; @@ -37,15 +36,16 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.state.DefaultDeviceStateService; @@ -154,7 +154,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { entitySubscriptions.values().removeIf(sub -> - !partitionService.resolve(ServiceType.TB_CORE, sub.getTenantId(), sub.getEntityId()).isMyPartition()); + !partitionService.isMyPartition(ServiceType.TB_CORE, sub.getTenantId(), sub.getEntityId())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 08a59973de..4e3d034207 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.sync.ie.EntityImportResult; import org.thingsboard.server.common.data.util.ThrowingRunnable; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.sync.ie.exporting.EntityExportService; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java index 7a7d176f81..35f2489f83 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java @@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.notification.rule.EscalatedNotificatio import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.sync.ie.EntityExportData; @@ -65,13 +67,24 @@ public class NotificationRuleExportService ruleChains = triggerConfig.getRuleChains(); if (ruleChains != null) { triggerConfig.setRuleChains(toExternalIds(ruleChains, RuleChainId::new, ctx).collect(Collectors.toSet())); } break; + } + case EDGE_CONNECTION: { + EdgeConnectionNotificationRuleTriggerConfig triggerConfig = (EdgeConnectionNotificationRuleTriggerConfig) ruleTriggerConfig; + triggerConfig.setEdges(null); + break; + } + case EDGE_COMMUNICATION_FAILURE: { + EdgeCommunicationFailureNotificationRuleTriggerConfig triggerConfig = (EdgeCommunicationFailureNotificationRuleTriggerConfig) ruleTriggerConfig; + triggerConfig.setEdges(null); + break; + } } NotificationRuleRecipientsConfig ruleRecipientsConfig = notificationRule.getRecipientsConfig(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java index 72f0fa219e..dd53e5b5bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java @@ -33,6 +33,8 @@ import org.thingsboard.server.common.data.notification.rule.EscalatedNotificatio import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; @@ -86,7 +88,7 @@ public class NotificationRuleImportService extends BaseEntityImportService ruleChains = triggerConfig.getRuleChains(); if (ruleChains != null) { @@ -95,6 +97,17 @@ public class NotificationRuleImportService extends BaseEntityImportService matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); - ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); - ArgumentMatcher matcherCustomerId = customerId == null ? - argument -> argument.getClass().equals(CustomerId.class) : argument -> argument.equals(customerId); - ArgumentMatcher matcherUserId = userId == null ? - argument -> argument.getClass().equals(UserId.class) : argument -> argument.equals(userId); - testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, matcherCustomerId, matcherUserId, userName, actionType, cntTime, - extractMatcherAdditionalInfo(additionalInfo)); - testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); - Mockito.reset(tbClusterService, auditLogService); - } - protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(HasName entity, HasName originator, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, @@ -624,7 +606,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { private String entityClassToString(HasName entity) { String className = entity.getClass().toString() .substring(entity.getClass().toString().lastIndexOf(".") + 1); - List str = className.chars() + List str = className.chars() .mapToObj(x -> (Character.isUpperCase(x)) ? "_" + Character.toString(x) : Character.toString(x)) .collect(Collectors.toList()); return String.join("", str).toUpperCase(Locale.ENGLISH).substring(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java index 488bd4ed70..458bb3240e 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -711,7 +711,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); Assert.assertTrue(edgeImitator.waitForResponses()); - Assert.assertTrue(onUpdateCallback.getSubscribeLatch().await(30, TimeUnit.SECONDS)); + Assert.assertTrue(onUpdateCallback.getSubscribeLatch().await(TIMEOUT, TimeUnit.SECONDS)); Assert.assertEquals(JacksonUtil.newObjectNode().put(attrKey, attrValue), JacksonUtil.fromBytes(onUpdateCallback.getPayloadBytes())); @@ -798,7 +798,21 @@ public class DeviceEdgeTest extends AbstractEdgeTest { // clean up stored edge events edgeEventService.cleanupEvents(1); - // perform rpc call to verify edgeId in DeviceActorMessageProcessor updated properly + // edge is disconnected: perform rpc call - no edge event saved + doPostAsync( + "/api/rpc/oneway/" + device.getId().getId().toString(), + JacksonUtil.toString(createDefaultRpc()), + String.class, + status().isOk()); + Awaitility.await() + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> { + PageData result = edgeEventService.findEdgeEvents(tenantId, tmpEdge.getId(), 0L, null, new TimePageLink(1)); + return result.getTotalElements() == 0; + }); + + // edge is connected: perform rpc call to verify edgeId in DeviceActorMessageProcessor updated properly + simulateEdgeActivation(tmpEdge); doPostAsync( "/api/rpc/oneway/" + device.getId().getId().toString(), JacksonUtil.toString(createDefaultRpc()), @@ -857,4 +871,23 @@ public class DeviceEdgeTest extends AbstractEdgeTest { return rpc; } + + private void simulateEdgeActivation(Edge edge) throws Exception { + ObjectNode attributes = JacksonUtil.newObjectNode(); + attributes.put("active", true); + doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + Awaitility.await() + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> { + List> values = doGetAsyncTyped("/api/plugins/telemetry/EDGE/" + edge.getId() + + "/values/attributes/SERVER_SCOPE", new TypeReference<>() {}); + Optional> activeAttrOpt = values.stream().filter(att -> att.get("key").equals("active")).findFirst(); + if (activeAttrOpt.isEmpty()) { + return false; + } + Map activeAttr = activeAttrOpt.get(); + return "true".equals(activeAttr.get("value").toString()); + }); + } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java index 4942dbdb9b..62d2c5eade 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java @@ -146,7 +146,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { } } - private void createRuleChainMetadata(RuleChain ruleChain) { + private RuleChainMetaData createRuleChainMetadata(RuleChain ruleChain) { RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); ruleChainMetaData.setRuleChainId(ruleChain.getId()); @@ -182,7 +182,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { ruleChainMetaData.addConnectionInfo(0, 2, "fail"); ruleChainMetaData.addConnectionInfo(1, 2, "success"); - doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class); + return doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class); } @Test @@ -193,9 +193,10 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { ruleChain.setType(RuleChainType.EDGE); RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); - edgeImitator.expectMessageAmount(1); + edgeImitator.expectMessageAmount(2); doPost("/api/edge/" + edge.getUuidId() + "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class); + RuleChainMetaData metaData = createRuleChainMetadata(savedRuleChain); Assert.assertTrue(edgeImitator.waitForMessages()); // set new rule chain as root @@ -213,6 +214,22 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Assert.assertTrue(ruleChainMsg.isRoot()); Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); + // update metadata for root rule chain + edgeImitator.expectMessageAmount(1); + metaData.getNodes().forEach(n -> n.setDebugMode(true)); + doPost("/api/ruleChain/metadata", metaData, RuleChainMetaData.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); + Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); + ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + ruleChainMsg = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); + Assert.assertNotNull(ruleChainMsg); + Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) || + UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType())); + Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); + Assert.assertEquals(savedRuleChain.getName(), ruleChainMsg.getName()); + Assert.assertTrue(ruleChainMsg.isRoot()); + // revert root rule chain edgeImitator.expectMessageAmount(1); doPost("/api/edge/" + edge.getUuidId() diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index f5e018ed3a..916e5790e9 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; import org.thingsboard.edge.rpc.EdgeGrpcClient; import org.thingsboard.edge.rpc.EdgeRpcClient; +import org.thingsboard.server.controller.AbstractWebTest; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; @@ -72,8 +73,6 @@ import java.util.stream.Collectors; @Slf4j public class EdgeImitator { - public static final int TIMEOUT_IN_SECONDS = 30; - private String routingKey; private String routingSecret; @@ -344,7 +343,7 @@ public class EdgeImitator { } public boolean waitForMessages() throws InterruptedException { - return waitForMessages(TIMEOUT_IN_SECONDS); + return waitForMessages(AbstractWebTest.TIMEOUT); } public boolean waitForMessages(int timeoutInSeconds) throws InterruptedException { @@ -359,7 +358,7 @@ public class EdgeImitator { } public boolean waitForResponses() throws InterruptedException { - return responsesLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS); + return responsesLatch.await(AbstractWebTest.TIMEOUT, TimeUnit.SECONDS); } public void expectResponsesAmount(int messageAmount) { diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index 14d017a7a4..7f8515f50b 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -315,7 +315,7 @@ public class HashPartitionServiceTest { .setPartitions(isolatedQueue.getPartitions()) .build(); - partitionService_common.updateQueue(queueUpdateMsg); + partitionService_common.updateQueues(List.of(queueUpdateMsg)); partitionService_common.recalculatePartitions(commonRuleEngine, List.of(dedicatedRuleEngine)); // expecting event about no partitions for isolated queue key verifyPartitionChangeEvent(event -> { @@ -323,7 +323,7 @@ public class HashPartitionServiceTest { return event.getPartitionsMap().get(queueKey).isEmpty(); }); - partitionService_dedicated.updateQueue(queueUpdateMsg); + partitionService_dedicated.updateQueues(List.of(queueUpdateMsg)); partitionService_dedicated.recalculatePartitions(dedicatedRuleEngine, List.of(commonRuleEngine)); verifyPartitionChangeEvent(event -> { QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, tenantId); @@ -342,7 +342,7 @@ public class HashPartitionServiceTest { .setQueueIdLSB(isolatedQueue.getUuidId().getLeastSignificantBits()) .setQueueName(isolatedQueue.getName()) .build(); - partitionService_dedicated.removeQueue(queueDeleteMsg); + partitionService_dedicated.removeQueues(List.of(queueDeleteMsg)); verifyPartitionChangeEvent(event -> { QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, tenantId); return event.getPartitionsMap().get(queueKey).isEmpty(); diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java index ce6ca753b3..d6bfbf1036 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.constructor; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -61,7 +60,7 @@ public class RuleChainMsgConstructorTest { } @Test - public void testConstructRuleChainMetadataUpdatedMsg_V_3_4_0() throws JsonProcessingException { + public void testConstructRuleChainMetadataUpdatedMsg_V_3_4_0() { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); @@ -80,7 +79,7 @@ public class RuleChainMsgConstructorTest { } @Test - public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_3() throws JsonProcessingException { + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_3() { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); @@ -120,7 +119,7 @@ public class RuleChainMsgConstructorTest { } @Test - public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0() throws JsonProcessingException { + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0() { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = @@ -161,7 +160,7 @@ public class RuleChainMsgConstructorTest { } @Test - public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0_inDifferentOrder() throws JsonProcessingException { + public void testConstructRuleChainMetadataUpdatedMsg_V_3_3_0_inDifferentOrder() { // same rule chain metadata, but different order of rule nodes RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder()); @@ -254,7 +253,7 @@ public class RuleChainMsgConstructorTest { return result; } - private List createRuleNodes(RuleChainId ruleChainId) throws JsonProcessingException { + private List createRuleNodes(RuleChainId ruleChainId) { List result = new ArrayList<>(); result.add(getOutputNode(ruleChainId)); result.add(getAcknowledgeNode(ruleChainId)); @@ -301,7 +300,7 @@ public class RuleChainMsgConstructorTest { return result; } - private List createRuleNodesInDifferentOrder(RuleChainId ruleChainId) throws JsonProcessingException { + private List createRuleNodesInDifferentOrder(RuleChainId ruleChainId) { List result = new ArrayList<>(); result.add(getPushToAnalyticsNode(ruleChainId)); result.add(getPushToCloudNode(ruleChainId)); @@ -319,99 +318,99 @@ public class RuleChainMsgConstructorTest { } - private RuleNode getOutputNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getOutputNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.flow.TbRuleChainOutputNode", "Output node", - JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":592}")); + JacksonUtil.toJsonNode("{\"version\":0}"), + JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":178,\"layoutY\":592}")); } - private RuleNode getCheckpointNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getCheckpointNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.flow.TbCheckpointNode", "Checkpoint node", - JacksonUtil.OBJECT_MAPPER.readTree("{\"queueName\":\"HighPriority\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}")); + JacksonUtil.toJsonNode("{\"queueName\":\"HighPriority\"}"), + JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":178,\"layoutY\":647}")); } - private RuleNode getSaveTimeSeriesNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getSaveTimeSeriesNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "Save Timeseries", - JacksonUtil.OBJECT_MAPPER.readTree("{\"defaultTTL\":0}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":823,\"layoutY\":157}")); + JacksonUtil.toJsonNode("{\"defaultTTL\":0}"), + JacksonUtil.toJsonNode("{\"layoutX\":823,\"layoutY\":157}")); } - private RuleNode getMessageTypeSwitchNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getMessageTypeSwitchNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "Message Type Switch", - JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":347,\"layoutY\":149}")); + JacksonUtil.toJsonNode("{\"version\":0}"), + JacksonUtil.toJsonNode("{\"layoutX\":347,\"layoutY\":149}")); } - private RuleNode getLogOtherNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getLogOtherNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.action.TbLogNode", "Log Other", - JacksonUtil.OBJECT_MAPPER.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":378}")); + JacksonUtil.toJsonNode("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), + JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":378}")); } - private RuleNode getPushToCloudNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getPushToCloudNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", "Push to cloud", - JacksonUtil.OBJECT_MAPPER.readTree("{\"scope\":\"SERVER_SCOPE\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":1129,\"layoutY\":52}")); + JacksonUtil.toJsonNode("{\"scope\":\"SERVER_SCOPE\"}"), + JacksonUtil.toJsonNode("{\"layoutX\":1129,\"layoutY\":52}")); } - private RuleNode getAcknowledgeNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getAcknowledgeNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.flow.TbAckNode", "Acknowledge node", - JacksonUtil.OBJECT_MAPPER.readTree("{\"version\":0}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":177,\"layoutY\":703}")); + JacksonUtil.toJsonNode("{\"version\":0}"), + JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":177,\"layoutY\":703}")); } - private RuleNode getDeviceProfileNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getDeviceProfileNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", "Device Profile Node", - JacksonUtil.OBJECT_MAPPER.readTree("{\"persistAlarmRulesState\":false,\"fetchAlarmRulesStateOnStart\":false}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \\\"Success\\\" relation type.\",\"layoutX\":187,\"layoutY\":468}")); + JacksonUtil.toJsonNode("{\"persistAlarmRulesState\":false,\"fetchAlarmRulesStateOnStart\":false}"), + JacksonUtil.toJsonNode("{\"description\":\"Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \\\"Success\\\" relation type.\",\"layoutX\":187,\"layoutY\":468}")); } - private RuleNode getSaveClientAttributesNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getSaveClientAttributesNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "Save Client Attributes", - JacksonUtil.OBJECT_MAPPER.readTree("{\"scope\":\"CLIENT_SCOPE\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":52}")); + JacksonUtil.toJsonNode("{\"scope\":\"CLIENT_SCOPE\"}"), + JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":52}")); } - private RuleNode getLogRpcFromDeviceNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getLogRpcFromDeviceNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.action.TbLogNode", "Log RPC from Device", - JacksonUtil.OBJECT_MAPPER.readTree("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":825,\"layoutY\":266}")); + JacksonUtil.toJsonNode("{\"jsScript\":\"return '\\\\nIncoming message:\\\\n' + JSON.stringify(msg) + '\\\\nIncoming metadata:\\\\n' + JSON.stringify(metadata);\"}"), + JacksonUtil.toJsonNode("{\"layoutX\":825,\"layoutY\":266}")); } - private RuleNode getRpcCallRequestNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getRpcCallRequestNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "RPC Call Request", - JacksonUtil.OBJECT_MAPPER.readTree("{\"timeoutInSeconds\":60}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"layoutX\":824,\"layoutY\":466}")); + JacksonUtil.toJsonNode("{\"timeoutInSeconds\":60}"), + JacksonUtil.toJsonNode("{\"layoutX\":824,\"layoutY\":466}")); } - private RuleNode getPushToAnalyticsNode(RuleChainId ruleChainId) throws JsonProcessingException { + private RuleNode getPushToAnalyticsNode(RuleChainId ruleChainId) { return createRuleNode(ruleChainId, "org.thingsboard.rule.engine.flow.TbRuleChainInputNode", "Push to Analytics", - JacksonUtil.OBJECT_MAPPER.readTree("{\"ruleChainId\":\"af588000-6c7c-11ec-bafd-c9a47a5c8d99\"}"), - JacksonUtil.OBJECT_MAPPER.readTree("{\"description\":\"\",\"layoutX\":477,\"layoutY\":560}")); + JacksonUtil.toJsonNode("{\"ruleChainId\":\"af588000-6c7c-11ec-bafd-c9a47a5c8d99\"}"), + JacksonUtil.toJsonNode("{\"description\":\"\",\"layoutX\":477,\"layoutY\":560}")); } -} \ No newline at end of file +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java index 9c45977a7c..835b39caf9 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -55,6 +55,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; @@ -209,6 +210,9 @@ public abstract class BaseEdgeProcessorTest { @MockBean protected AttributesService attributesService; + @MockBean + protected TimeseriesService timeseriesService; + @MockBean protected TbClusterService tbClusterService; diff --git a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 9917e837b9..2570b80f9f 100644 --- a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java @@ -20,6 +20,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.server.cache.limits.DefaultRateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.NotificationRuleId; @@ -28,9 +30,7 @@ import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.util.limits.DefaultRateLimitService; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.dao.tenant.DefaultTbTenantProfileCache; import java.util.List; import java.util.UUID; @@ -46,12 +46,12 @@ import static org.mockito.Mockito.when; public class RateLimitServiceTest { private RateLimitService rateLimitService; - private TbTenantProfileCache tenantProfileCache; + private DefaultTbTenantProfileCache tenantProfileCache; private TenantId tenantId; @Before public void beforeEach() { - tenantProfileCache = Mockito.mock(TbTenantProfileCache.class); + tenantProfileCache = Mockito.mock(DefaultTbTenantProfileCache.class); rateLimitService = new DefaultRateLimitService(tenantProfileCache, mock(NotificationRuleProcessor.class), 60, 100); tenantId = new TenantId(UUID.randomUUID()); } @@ -69,6 +69,8 @@ public class RateLimitServiceTest { profileConfiguration.setCustomerServerRestLimitsConfiguration(rateLimit); profileConfiguration.setWsUpdatesPerSessionRateLimit(rateLimit); profileConfiguration.setCassandraQueryTenantRateLimitsConfiguration(rateLimit); + profileConfiguration.setEdgeEventRateLimits(rateLimit); + profileConfiguration.setEdgeEventRateLimitsPerEdge(rateLimit); updateTenantProfileConfiguration(profileConfiguration); for (LimitedApi limitedApi : List.of( @@ -76,7 +78,9 @@ public class RateLimitServiceTest { LimitedApi.ENTITY_IMPORT, LimitedApi.NOTIFICATION_REQUESTS, LimitedApi.REST_REQUESTS_PER_CUSTOMER, - LimitedApi.CASSANDRA_QUERIES + LimitedApi.CASSANDRA_QUERIES, + LimitedApi.EDGE_EVENTS, + LimitedApi.EDGE_EVENTS_PER_EDGE )) { testRateLimits(limitedApi, max, tenantId); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index ed4a1c613a..1e95f9add3 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -93,7 +93,7 @@ import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.notification.DefaultNotificationDeduplicationService; import org.thingsboard.server.service.notification.rule.cache.DefaultNotificationRulesCache; import org.thingsboard.server.service.state.DeviceStateService; diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 6efb171fb4..91d4f3ef53 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -41,6 +41,7 @@ import org.thingsboard.server.service.gateway_device.GatewayNotificationsService import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; +import java.util.List; import java.util.UUID; import static org.mockito.ArgumentMatchers.any; @@ -95,7 +96,7 @@ public class DefaultTbClusterServiceTest { when(producerProvider.getRuleEngineNotificationsMsgProducer()).thenReturn(tbQueueProducer); - clusterService.onQueueChange(createTestQueue()); + clusterService.onQueuesUpdate(List.of(createTestQueue())); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); @@ -120,7 +121,7 @@ public class DefaultTbClusterServiceTest { when(producerProvider.getRuleEngineNotificationsMsgProducer()).thenReturn(tbQueueProducer); - clusterService.onQueueChange(createTestQueue()); + clusterService.onQueuesUpdate(List.of(createTestQueue())); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); @@ -148,7 +149,7 @@ public class DefaultTbClusterServiceTest { when(producerProvider.getRuleEngineNotificationsMsgProducer()).thenReturn(tbREQueueProducer); when(producerProvider.getTransportNotificationsMsgProducer()).thenReturn(tbTransportQueueProducer); - clusterService.onQueueChange(createTestQueue()); + clusterService.onQueuesUpdate(List.of(createTestQueue())); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, TRANSPORT); @@ -194,7 +195,7 @@ public class DefaultTbClusterServiceTest { when(producerProvider.getTbCoreNotificationsMsgProducer()).thenReturn(tbCoreQueueProducer); when(producerProvider.getTransportNotificationsMsgProducer()).thenReturn(tbTransportQueueProducer); - clusterService.onQueueChange(createTestQueue()); + clusterService.onQueuesUpdate(List.of(createTestQueue())); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index b01ffe0d72..92959e4552 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -42,21 +42,6 @@ queue.transport.poll_interval=5 queue.core.poll-interval=5 queue.core.partitions=2 queue.rule-engine.poll-interval=5 -queue.rule-engine.queues[0].poll-interval=5 -queue.rule-engine.queues[0].partitions=2 -queue.rule-engine.queues[0].processing-strategy.retries=1 -queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[1].poll-interval=5 -queue.rule-engine.queues[1].partitions=2 -queue.rule-engine.queues[1].processing-strategy.retries=1 -queue.rule-engine.queues[1].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[1].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[2].poll-interval=5 -queue.rule-engine.queues[2].partitions=2 -queue.rule-engine.queues[2].processing-strategy.retries=1 -queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0 queue.rule-engine.stats.enabled=true usage.stats.report.enabled=false diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 6b9544e761..184aa615ba 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -40,6 +40,10 @@ org.thingsboard.common data + + org.thingsboard.common + message + org.springframework.boot spring-boot-autoconfigure diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java b/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java similarity index 92% rename from dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java rename to common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java index bcd560a6f9..f3530e99d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.util.limits; +package org.thingsboard.server.cache.limits; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -28,25 +28,25 @@ import org.thingsboard.server.common.data.exception.TenantProfileNotFoundExcepti import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.limit.LimitedApi; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.tools.TbRateLimits; -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import java.util.concurrent.TimeUnit; +@Lazy @Service @Slf4j public class DefaultRateLimitService implements RateLimitService { - private final TbTenantProfileCache tenantProfileCache; + private final TenantProfileProvider tenantProfileProvider; private final NotificationRuleProcessor notificationRuleProcessor; - public DefaultRateLimitService(TbTenantProfileCache tenantProfileCache, + public DefaultRateLimitService(TenantProfileProvider tenantProfileProvider, @Lazy NotificationRuleProcessor notificationRuleProcessor, @Value("${cache.rateLimits.timeToLiveInMinutes:120}") int rateLimitsTtl, @Value("${cache.rateLimits.maxSize:200000}") int rateLimitsCacheMaxSize) { - this.tenantProfileCache = tenantProfileCache; + this.tenantProfileProvider = tenantProfileProvider; this.notificationRuleProcessor = notificationRuleProcessor; this.rateLimits = Caffeine.newBuilder() .expireAfterAccess(rateLimitsTtl, TimeUnit.MINUTES) @@ -66,7 +66,7 @@ public class DefaultRateLimitService implements RateLimitService { if (tenantId.isSysTenantId()) { return true; } - TenantProfile tenantProfile = tenantProfileCache.get(tenantId); + TenantProfile tenantProfile = tenantProfileProvider.get(tenantId); if (tenantProfile == null) { throw new TenantProfileNotFoundException(tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java b/common/cache/src/main/java/org/thingsboard/server/cache/limits/RateLimitService.java similarity index 95% rename from dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java rename to common/cache/src/main/java/org/thingsboard/server/cache/limits/RateLimitService.java index e24383bb62..84c22c514b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/limits/RateLimitService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.util.limits; +package org.thingsboard.server.cache.limits; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.limit.LimitedApi; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java b/common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java similarity index 66% rename from common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java rename to common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java index 8edb224fec..15243d12c8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java @@ -13,18 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.queue.settings; +package org.thingsboard.server.cache.limits; -import lombok.Data; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantId; -@Data -@Deprecated -public class TbRuleEngineQueueAckStrategyConfiguration { +public interface TenantProfileProvider { - private String type; - private int retries; - private double failurePercentage; - private long pauseBetweenRetries; - private long maxPauseBetweenRetries; + TenantProfile get(TenantId tenantId); } diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java index a4502da4b1..11b60c86bd 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueClusterService.java @@ -17,8 +17,12 @@ package org.thingsboard.server.queue; import org.thingsboard.server.common.data.queue.Queue; +import java.util.List; + public interface TbQueueClusterService { - void onQueueChange(Queue queue); - void onQueueDelete(Queue queue); + void onQueuesUpdate(List queues); + + void onQueuesDelete(List queues); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java index 08dd64275a..2e65a58647 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java @@ -38,7 +38,7 @@ public interface AuditLogService { PageData findAuditLogsByTenantId(TenantId tenantId, List actionTypes, TimePageLink pageLink); - ListenableFuture> logEntityAction( + ListenableFuture logEntityAction( TenantId tenantId, CustomerId customerId, UserId userId, diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 15e55d8713..d6d5551ccf 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -91,4 +91,6 @@ public interface EdgeService extends EntityDaoService { PageData findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink); String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId, String tbRuleChainInputNodeClassName); + + ListenableFuture isEdgeActiveAsync(TenantId tenantId, EdgeId edgeId, String activityState); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java index 219b4163f9..9b0d5a280d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java @@ -43,5 +43,4 @@ public interface EventService { void cleanupEvents(long regularEventExpTs, long debugEventExpTs, boolean cleanupDb); - void migrateEvents(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index 5f743f626a..980eb880a0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -31,6 +31,8 @@ public enum LimitedApi { REST_REQUESTS_PER_CUSTOMER(DefaultTenantProfileConfiguration::getCustomerServerRestLimitsConfiguration, "REST API requests per customer", false), WS_UPDATES_PER_SESSION(DefaultTenantProfileConfiguration::getWsUpdatesPerSessionRateLimit, "WS updates per session", true), CASSANDRA_QUERIES(DefaultTenantProfileConfiguration::getCassandraQueryTenantRateLimitsConfiguration, "Cassandra queries", true), + EDGE_EVENTS(DefaultTenantProfileConfiguration::getEdgeEventRateLimits, "Edge events", true), + EDGE_EVENTS_PER_EDGE(DefaultTenantProfileConfiguration::getEdgeEventRateLimitsPerEdge, "Edge events per edge", false), PASSWORD_RESET(false, true), TWO_FA_VERIFICATION_CODE_SEND(false, true), TWO_FA_VERIFICATION_CODE_CHECK(false, true), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index 8eb4451e3b..cfc91d8fca 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -28,6 +28,7 @@ public enum NotificationType { ENTITIES_LIMIT, API_USAGE_LIMIT, RULE_NODE, - RATE_LIMITS - + RATE_LIMITS, + EDGE_CONNECTION, + EDGE_COMMUNICATION_FAILURE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeCommunicationFailureNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeCommunicationFailureNotificationInfo.java new file mode 100644 index 0000000000..6d0f2ae5f5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeCommunicationFailureNotificationInfo.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.info; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeCommunicationFailureNotificationInfo implements RuleOriginatedNotificationInfo { + + private TenantId tenantId; + private CustomerId customerId; + private EdgeId edgeId; + private String edgeName; + private String failureMsg; + + @Override + public Map getTemplateData() { + return mapOf( + "edgeId", edgeId.toString(), + "edgeName", edgeName, + "failureMsg", failureMsg + ); + } + + @Override + public TenantId getAffectedTenantId() { + return tenantId; + } + + @Override + public CustomerId getAffectedCustomerId() { + return customerId; + } + + @Override + public EntityId getStateEntityId() { + return edgeId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java new file mode 100644 index 0000000000..62b1370566 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.info; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeConnectionNotificationInfo implements RuleOriginatedNotificationInfo { + + private String eventType; + private TenantId tenantId; + private CustomerId customerId; + private EdgeId edgeId; + private String edgeName; + + @Override + public Map getTemplateData() { + return mapOf( + "eventType", eventType, + "edgeId", edgeId.toString(), + "edgeName", edgeName + ); + } + + @Override + public TenantId getAffectedTenantId() { + return tenantId; + } + + @Override + public CustomerId getAffectedCustomerId() { + return customerId; + } + + @Override + public EntityId getStateEntityId() { + return edgeId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java new file mode 100644 index 0000000000..6212c22a0d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +import java.util.concurrent.TimeUnit; + +@Data +@Builder +public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final CustomerId customerId; + private final EdgeId edgeId; + private final String edgeName; + private final String failureMsg; + private final String error; + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), error); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.MINUTES.toMillis(30); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; + } + + @Override + public EntityId getOriginatorEntityId() { + return edgeId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java new file mode 100644 index 0000000000..766338dba6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +import java.util.concurrent.TimeUnit; + +@Data +@Builder +public class EdgeConnectionTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final CustomerId customerId; + private final EdgeId edgeId; + private final boolean connected; + private final String edgeName; + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), String.valueOf(connected)); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.MINUTES.toMillis(1); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.EDGE_CONNECTION; + } + + @Override + public EntityId getOriginatorEntityId() { + return edgeId; + } +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java similarity index 53% rename from common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java index 8199977618..595b0fa7d5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java @@ -13,21 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.queue.settings; +package org.thingsboard.server.common.data.notification.rule.trigger.config; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; +import java.util.UUID; @Data -@Deprecated -public class TbRuleEngineQueueConfiguration { +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeCommunicationFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set edges; // if empty - all edges - private String name; - private String topic; - private int pollInterval; - private int partitions; - private boolean consumerPerPartition; - private long packProcessingTimeout; - private TbRuleEngineQueueSubmitStrategyConfiguration submitStrategy; - private TbRuleEngineQueueAckStrategyConfiguration processingStrategy; + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlEventTsColumn.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java similarity index 50% rename from application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlEventTsColumn.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java index 4ceea39cf2..8c0905cc59 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlEventTsColumn.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java @@ -13,28 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.install.migrate; +package org.thingsboard.server.common.data.notification.rule.trigger.config; -import com.datastax.oss.driver.api.core.cql.Row; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.Set; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF; +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeConnectionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { -public class CassandraToSqlEventTsColumn extends CassandraToSqlColumn { - - CassandraToSqlEventTsColumn() { - super("id", "ts", CassandraToSqlColumnType.BIGINT, null, false); - } + private Set edges; // if empty - all edges + private Set notifyOn; @Override - public String getColumnValue(Row row) { - UUID id = row.getUuid(getIndex()); - long ts = getTs(id); - return ts + ""; + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_CONNECTION; } - private long getTs(UUID uuid) { - return (uuid.timestamp() - EPOCH_DIFF) / 10000; + public enum EdgeConnectivityEvent { + CONNECTED, DISCONNECTED } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java index 5ffb8a5fe1..15a5e59255 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java @@ -36,6 +36,8 @@ import java.io.Serializable; @Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT"), @Type(value = ApiUsageLimitNotificationRuleTriggerConfig.class, name = "API_USAGE_LIMIT"), @Type(value = RateLimitsNotificationRuleTriggerConfig.class, name = "RATE_LIMITS"), + @Type(value = EdgeConnectionNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTION"), + @Type(value = EdgeCommunicationFailureNotificationRuleTriggerConfig.class, name = "EDGE_COMMUNICATION_FAILURE"), }) public interface NotificationRuleTriggerConfig extends Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java index 6eff7a8114..8469fac752 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java @@ -26,6 +26,8 @@ public enum NotificationRuleTriggerType { ALARM_ASSIGNMENT, DEVICE_ACTIVITY, RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, + EDGE_CONNECTION, + EDGE_COMMUNICATION_FAILURE, NEW_PLATFORM_VERSION(false), ENTITIES_LIMIT(false), API_USAGE_LIMIT(false), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 1a4df035f5..3fe110de0b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -81,6 +81,9 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private String cassandraQueryTenantRateLimitsConfiguration; + private String edgeEventRateLimits; + private String edgeEventRateLimitsPerEdge; + private int defaultStorageTtlDays; private int alarmsTtlDays; private int rpcTtlDays; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/TemplateUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/TemplateUtils.java index 3bc2aac1f8..a6b1c11d42 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/util/TemplateUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/TemplateUtils.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Map; import java.util.function.UnaryOperator; +import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.google.common.base.Strings.nullToEmpty; @@ -49,7 +50,7 @@ public class TemplateUtils { value = FUNCTIONS.get(function).apply(value); } } - return value; + return Matcher.quoteReplacement(value); }); } diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 4da2883291..04e3b64570 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -1280,8 +1280,8 @@ message ToCoreNotificationMsg { FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; bytes componentLifecycleMsg = 3 [deprecated = true]; bytes edgeEventUpdateMsg = 4 [deprecated = true]; - QueueUpdateMsg queueUpdateMsg = 5; - QueueDeleteMsg queueDeleteMsg = 6; + repeated QueueUpdateMsg queueUpdateMsgs = 5; + repeated QueueDeleteMsg queueDeleteMsgs = 6; VersionControlResponseMsg vcResponseMsg = 7; bytes toEdgeSyncRequestMsg = 8 [deprecated = true]; bytes fromEdgeSyncResponseMsg = 9 [deprecated = true]; @@ -1307,8 +1307,8 @@ message ToRuleEngineMsg { message ToRuleEngineNotificationMsg { bytes componentLifecycleMsg = 1 [deprecated = true]; FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; - QueueUpdateMsg queueUpdateMsg = 3; - QueueDeleteMsg queueDeleteMsg = 4; + repeated QueueUpdateMsg queueUpdateMsgs = 3; + repeated QueueDeleteMsg queueDeleteMsgs = 4; ComponentLifecycleMsgProto componentLifecycle = 5; } @@ -1328,8 +1328,8 @@ message ToTransportMsg { ResourceUpdateMsg resourceUpdateMsg = 12; ResourceDeleteMsg resourceDeleteMsg = 13; UplinkNotificationMsg uplinkNotificationMsg = 14; - QueueUpdateMsg queueUpdateMsg = 15; - QueueDeleteMsg queueDeleteMsg = 16; + repeated QueueUpdateMsg queueUpdateMsgs = 15; + repeated QueueDeleteMsg queueDeleteMsgs = 16; } message UsageStatsKVProto{ diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 6caf17aeef..4630dc4bf8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -171,27 +171,35 @@ public class HashPartitionService implements PartitionService { } @Override - public void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { - TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId); - partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic()); - partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions()); - myPartitions.remove(queueKey); - if (!tenantId.isSysTenantId()) { - tenantRoutingInfoMap.remove(tenantId); + public void updateQueues(List queueUpdateMsgs) { + for (TransportProtos.QueueUpdateMsg queueUpdateMsg : queueUpdateMsgs) { + TenantId tenantId = TenantId.fromUUID(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId); + partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic()); + partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions()); + if (!tenantId.isSysTenantId()) { + tenantRoutingInfoMap.remove(tenantId); + } } } @Override - public void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { - TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); - myPartitions.remove(queueKey); - partitionTopicsMap.remove(queueKey); - partitionSizesMap.remove(queueKey); - evictTenantInfo(tenantId); + public void removeQueues(List queueDeleteMsgs) { + List queueKeys = queueDeleteMsgs.stream() + .map(queueDeleteMsg -> { + TenantId tenantId = TenantId.fromUUID(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); + return new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); + }) + .collect(Collectors.toList()); + queueKeys.forEach(queueKey -> { + myPartitions.remove(queueKey); + partitionTopicsMap.remove(queueKey); + partitionSizesMap.remove(queueKey); + evictTenantInfo(queueKey.getTenantId()); + }); if (serviceInfoProvider.isService(ServiceType.TB_RULE_ENGINE)) { - publishPartitionChangeEvent(ServiceType.TB_RULE_ENGINE, Map.of(queueKey, Collections.emptySet())); + publishPartitionChangeEvent(ServiceType.TB_RULE_ENGINE, queueKeys.stream() + .collect(Collectors.toMap(k -> k, k -> Collections.emptySet()))); } } @@ -251,7 +259,12 @@ public class HashPartitionService implements PartitionService { @Override public boolean isMyPartition(ServiceType serviceType, TenantId tenantId, EntityId entityId) { - return resolve(serviceType, tenantId, entityId).isMyPartition(); + try { + return resolve(serviceType, tenantId, entityId).isMyPartition(); + } catch (TenantNotFoundException e) { + log.warn("Tenant with id {} not found", tenantId, new RuntimeException("stacktrace")); + return false; + } } private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { @@ -316,13 +329,11 @@ public class HashPartitionService implements PartitionService { .forEach(removed::add); } removed.forEach(queueKey -> { - log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", queueKey); changedPartitionsMap.put(queueKey, Collections.emptySet()); }); myPartitions.forEach((queueKey, partitions) -> { if (!partitions.equals(oldPartitions.get(queueKey))) { - log.info("[{}] NEW PARTITIONS: {}", queueKey, partitions); Set tpiList = partitions.stream() .map(partition -> buildTopicPartitionInfo(queueKey, partition)) .collect(Collectors.toSet()); @@ -368,15 +379,17 @@ public class HashPartitionService implements PartitionService { } private void publishPartitionChangeEvent(ServiceType serviceType, Map> partitionsMap) { - if (log.isDebugEnabled()) { - log.debug("Publishing partition change event for service type " + serviceType + ":" + System.lineSeparator() + - partitionsMap.entrySet().stream() - .map(entry -> entry.getKey() + " - " + entry.getValue().stream() - .map(TopicPartitionInfo::getFullTopicName).sorted() - .collect(Collectors.toList())) - .collect(Collectors.joining(System.lineSeparator()))); + log.info("Partitions changed: {}", System.lineSeparator() + partitionsMap.entrySet().stream() + .map(entry -> "[" + entry.getKey() + "] - [" + entry.getValue().stream() + .map(tpi -> tpi.getPartition().orElse(-1).toString()).sorted() + .collect(Collectors.joining(", ")) + "]") + .collect(Collectors.joining(System.lineSeparator()))); + PartitionChangeEvent event = new PartitionChangeEvent(this, serviceType, partitionsMap); + try { + applicationEventPublisher.publishEvent(event); + } catch (Exception e) { + log.error("Failed to publish partition change event {}", event, e); } - applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceType, partitionsMap)); } @Override @@ -480,7 +493,7 @@ public class HashPartitionService implements PartitionService { } private void logServiceInfo(TransportProtos.ServiceInfo server) { - log.info("[{}] Found common server: [{}]", server.getServiceId(), server.getServiceTypesList()); + log.info("[{}] Found common server: {}", server.getServiceId(), server.getServiceTypesList()); } private void addNode(Map> queueServiceList, ServiceInfo instance) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java index 2b2479f59b..e14a6c3947 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java @@ -63,9 +63,9 @@ public interface PartitionService { int countTransportsByType(String type); - void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg); + void updateQueues(List queueUpdateMsgs); - void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg); + void removeQueues(List queueDeleteMsgs); void removeTenant(TenantId tenantId); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java index 42039bf417..2d6d707731 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbApplicationEventListener.java @@ -41,7 +41,11 @@ public abstract class TbApplicationEventListener i seqNumberLock.unlock(); } if (validUpdate && filterTbApplicationEvent(event)) { - onTbApplicationEvent(event); + try { + onTbApplicationEvent(event); + } catch (Exception e) { + log.error("Failed to handle partition change event: {}", event, e); + } } else { log.info("Application event ignored due to invalid sequence number ({} > {}). Event: {}", lastProcessedSequenceNumber, event.getSequenceNumber(), event); } diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java index a59013bbfe..6e30b06a2c 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java @@ -152,12 +152,14 @@ public class SnmpTransportContext extends TransportContext { try { if (!newProfileTransportConfiguration.equals(sessionContext.getProfileTransportConfiguration())) { sessionContext.setProfileTransportConfiguration(newProfileTransportConfiguration); + sessionContext.setDevice(device); sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); snmpTransportService.cancelQueryingTasks(sessionContext); snmpTransportService.createQueryingTasks(sessionContext); transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.UPDATED, true, null); } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) { sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration); + sessionContext.setDevice(device); sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.UPDATED, true, null); } else { diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java index 3a8811df97..3dc293b70b 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java @@ -15,6 +15,11 @@ */ package org.thingsboard.server.transport.snmp.service; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableScheduledFuture; +import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.Builder; @@ -32,7 +37,7 @@ import org.snmp4j.mp.MPv3; import org.snmp4j.security.SecurityModels; import org.snmp4j.security.SecurityProtocols; import org.snmp4j.security.USM; -import org.snmp4j.smi.Address; +import org.snmp4j.smi.IpAddress; import org.snmp4j.smi.OctetString; import org.snmp4j.smi.TcpAddress; import org.snmp4j.smi.UdpAddress; @@ -44,6 +49,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.kv.DataType; @@ -53,11 +59,11 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbSnmpTransportComponent; import org.thingsboard.server.transport.snmp.SnmpTransportContext; import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; +import org.thingsboard.server.transport.snmp.session.ScheduledTask; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -71,8 +77,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -80,6 +84,7 @@ import java.util.stream.Collectors; @Service @Slf4j @RequiredArgsConstructor +@SuppressWarnings("UnstableApiUsage") public class SnmpTransportService implements TbTransportService, CommandResponder { private final TransportService transportService; private final PduService pduService; @@ -88,23 +93,27 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @Getter private Snmp snmp; - private ScheduledExecutorService queryingExecutor; - private ExecutorService responseProcessingExecutor; + private ListeningScheduledExecutorService scheduler; + private ExecutorService executor; private final Map responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class); private final Map responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class); @Value("${transport.snmp.bind_port:1620}") private Integer snmpBindPort; - @Value("${transport.snmp.response_processing.parallelism_level}") - private Integer responseProcessingParallelismLevel; + @Value("${transport.snmp.response_processing.parallelism_level:4}") + private int responseProcessingThreadPoolSize; + @Value("${transport.snmp.scheduler_thread_pool_size:4}") + private int schedulerThreadPoolSize; @Value("${transport.snmp.underlying_protocol}") private String snmpUnderlyingProtocol; + @Value("${transport.snmp.request_chunk_delay_ms:100}") + private int requestChunkDelayMs; @PostConstruct private void init() throws IOException { - queryingExecutor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), ThingsBoardThreadFactory.forName("snmp-querying")); - responseProcessingExecutor = ThingsBoardExecutors.newWorkStealingPool(responseProcessingParallelismLevel, "snmp-response-processing"); + scheduler = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(schedulerThreadPoolSize, ThingsBoardThreadFactory.forName("snmp-querying"))); + executor = ThingsBoardExecutors.newWorkStealingPool(responseProcessingThreadPoolSize, "snmp-response-processing"); initializeSnmp(); configureResponseDataMappers(); @@ -115,11 +124,11 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @PreDestroy public void stop() { - if (queryingExecutor != null) { - queryingExecutor.shutdownNow(); + if (scheduler != null) { + scheduler.shutdownNow(); } - if (responseProcessingExecutor != null) { - responseProcessingExecutor.shutdownNow(); + if (executor != null) { + executor.shutdownNow(); } } @@ -144,38 +153,39 @@ public class SnmpTransportService implements TbTransportService, CommandResponde } public void createQueryingTasks(DeviceSessionContext sessionContext) { - List> queryingTasks = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() + sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() .filter(communicationConfig -> communicationConfig instanceof RepeatingQueryingSnmpCommunicationConfig) - .map(config -> { + .forEach(config -> { RepeatingQueryingSnmpCommunicationConfig repeatingCommunicationConfig = (RepeatingQueryingSnmpCommunicationConfig) config; Long queryingFrequency = repeatingCommunicationConfig.getQueryingFrequencyMs(); - return queryingExecutor.scheduleWithFixedDelay(() -> { + ScheduledTask scheduledTask = new ScheduledTask(); + scheduledTask.init(() -> { try { if (sessionContext.isActive()) { - sendRequest(sessionContext, repeatingCommunicationConfig); + return sendRequest(sessionContext, repeatingCommunicationConfig); } } catch (Exception e) { log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString()); transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), config.getSpec().getLabel(), e); } - }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS); - }) - .collect(Collectors.toList()); - sessionContext.getQueryingTasks().addAll(queryingTasks); + return Futures.immediateVoidFuture(); + }, queryingFrequency, scheduler); + sessionContext.getQueryingTasks().add(scheduledTask); + }); } public void cancelQueryingTasks(DeviceSessionContext sessionContext) { - sessionContext.getQueryingTasks().forEach(task -> task.cancel(true)); + sessionContext.getQueryingTasks().forEach(ScheduledTask::cancel); sessionContext.getQueryingTasks().clear(); } - private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) { - sendRequest(sessionContext, communicationConfig, Collections.emptyMap()); + private ListenableFuture sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) { + return sendRequest(sessionContext, communicationConfig, Collections.emptyMap()); } - private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) { + private ListenableFuture sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) { List request = pduService.createPdus(sessionContext, communicationConfig, values); RequestContext requestContext = RequestContext.builder() .communicationSpec(communicationConfig.getSpec()) @@ -183,19 +193,40 @@ public class SnmpTransportService implements TbTransportService, CommandResponde .responseMappings(communicationConfig.getAllMappings()) .requestSize(request.size()) .build(); - sendRequest(sessionContext, request, requestContext); + return sendRequest(sessionContext, request, requestContext); } - private void sendRequest(DeviceSessionContext sessionContext, List request, RequestContext requestContext) { - for (PDU pdu : request) { - log.debug("Executing SNMP request for device {} with {} variable bindings", sessionContext.getDeviceId(), pdu.size()); - try { - snmp.send(pdu, sessionContext.getTarget(), requestContext, sessionContext); - } catch (IOException e) { - log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString()); - transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), e); + private ListenableFuture sendRequest(DeviceSessionContext sessionContext, List request, RequestContext requestContext) { + if (request.size() <= 1 || requestChunkDelayMs == 0) { + for (PDU pdu : request) { + sendPdu(pdu, requestContext, sessionContext); + } + return Futures.immediateVoidFuture(); + } + + List> futures = new ArrayList<>(); + for (int i = 0, delay = 0; i < request.size(); i++, delay += requestChunkDelayMs) { + PDU pdu = request.get(i); + if (delay == 0) { + sendPdu(pdu, requestContext, sessionContext); + } else { + ListenableScheduledFuture future = scheduler.schedule(() -> { + sendPdu(pdu, requestContext, sessionContext); + }, delay, TimeUnit.MILLISECONDS); + futures.add(future); } } + return Futures.whenAllComplete(futures).call(() -> null, MoreExecutors.directExecutor()); + } + + private void sendPdu(PDU pdu, RequestContext requestContext, DeviceSessionContext sessionContext) { + log.debug("[{}] Sending SNMP request with {} variable bindings to {}", sessionContext.getDeviceId(), pdu.size(), sessionContext.getTarget().getAddress()); + try { + snmp.send(pdu, sessionContext.getTarget(), requestContext, sessionContext); + } catch (Exception e) { + log.error("[{}] Failed to send SNMP request", sessionContext.getDeviceId(), e); + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), e); + } } public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) { @@ -251,21 +282,19 @@ public class SnmpTransportService implements TbTransportService, CommandResponde ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext); RequestContext requestContext = (RequestContext) event.getUserObject(); if (event.getError() != null) { - log.warn("SNMP response error: {}", event.getError().toString()); + log.warn("[{}] SNMP response error: {}", sessionContext.getDeviceId(), event.getError().toString()); transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), new RuntimeException(event.getError())); return; } PDU responsePdu = event.getResponse(); - if (log.isTraceEnabled()) { - log.trace("Received PDU for device {}: {}", sessionContext.getDeviceId(), responsePdu); - } + log.trace("[{}] Received PDU: {}", sessionContext.getDeviceId(), responsePdu); List response; if (requestContext.getRequestSize() == 1) { if (responsePdu == null) { - log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID()); if (requestContext.getMethod() == SnmpMethod.GET) { + log.debug("[{}][{}] Empty response from device", sessionContext.getDeviceId(), event.getRequest().getRequestID()); transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), new RuntimeException("No response from device")); } return; @@ -281,14 +310,14 @@ public class SnmpTransportService implements TbTransportService, CommandResponde response.add(responsePart); } } - log.debug("All response parts are collected for request to device {}", sessionContext.getDeviceId()); + log.debug("[{}] All {} response parts are collected for request", sessionContext.getDeviceId(), responseParts.size()); } else { - log.trace("Awaiting other response parts for request to device {}", sessionContext.getDeviceId()); + log.trace("[{}] Awaiting other response parts for request", sessionContext.getDeviceId()); return; } } - responseProcessingExecutor.execute(() -> { + executor.execute(() -> { try { processResponse(sessionContext, response, requestContext); } catch (Exception e) { @@ -298,24 +327,31 @@ public class SnmpTransportService implements TbTransportService, CommandResponde } /* - * SNMP notifications handler - * - * TODO: add check for host uniqueness when saving device (for backward compatibility - only for the ones using from-device RPC requests) - * - * NOTE: SNMP TRAPs support won't work properly when there is more than one SNMP transport, - * due to load-balancing of requests from devices: session might not be on this instance - * */ + * SNMP notifications handler + * + * TODO: add check for host uniqueness when saving device (for backward compatibility - only for the ones using from-device RPC requests) + * + * NOTE: SNMP TRAPs support won't work properly when there is more than one SNMP transport, + * due to load-balancing of requests from devices: session might not be on this instance + * */ @Override public void processPdu(CommandResponderEvent event) { - Address sourceAddress = event.getPeerAddress(); - DeviceSessionContext sessionContext = transportContext.getSessions().stream() - .filter(session -> session.getTarget().getAddress().equals(sourceAddress)) - .findFirst().orElse(null); - if (sessionContext == null) { - log.warn("SNMP TRAP processing failed: couldn't find device session for address {}", sourceAddress); + IpAddress sourceAddress = (IpAddress) event.getPeerAddress(); + List sessions = transportContext.getSessions().stream() + .filter(session -> ((IpAddress) session.getTarget().getAddress()).getInetAddress().equals(sourceAddress.getInetAddress())) + .collect(Collectors.toList()); + if (sessions.isEmpty()) { + log.warn("Couldn't find device session for SNMP TRAP for address {}", sourceAddress); + return; + } else if (sessions.size() > 1) { + for (DeviceSessionContext sessionContext : sessions) { + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST.getLabel(), + new IllegalStateException("Found multiple devices for host " + sourceAddress.getInetAddress().getHostAddress())); + } return; } + DeviceSessionContext sessionContext = sessions.get(0); try { processIncomingTrap(sessionContext, event); } catch (Throwable e) { @@ -327,11 +363,11 @@ public class SnmpTransportService implements TbTransportService, CommandResponde private void processIncomingTrap(DeviceSessionContext sessionContext, CommandResponderEvent event) { PDU pdu = event.getPDU(); if (pdu == null) { - log.warn("Got empty trap from device {}", sessionContext.getDeviceId()); + log.warn("[{}] Received empty SNMP trap", sessionContext.getDeviceId()); throw new IllegalArgumentException("Received TRAP with no data"); } - log.debug("Processing SNMP trap from device {} (PDU: {}}", sessionContext.getDeviceId(), pdu); + log.debug("[{}] Processing SNMP trap: {}", sessionContext.getDeviceId(), pdu); SnmpCommunicationConfig communicationConfig = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() .filter(config -> config.getSpec() == SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST).findFirst() .orElseThrow(() -> new IllegalArgumentException("No config found for to-server RPC requests")); @@ -341,7 +377,7 @@ public class SnmpTransportService implements TbTransportService, CommandResponde .method(SnmpMethod.TRAP) .build(); - responseProcessingExecutor.execute(() -> { + executor.execute(() -> { processResponse(sessionContext, List.of(pdu), requestContext); }); } @@ -352,7 +388,7 @@ public class SnmpTransportService implements TbTransportService, CommandResponde JsonObject responseData = responseDataMappers.get(requestContext.getCommunicationSpec()).map(response, requestContext); if (responseData.size() == 0) { - log.warn("No values in the SNMP response for device {}", sessionContext.getDeviceId()); + log.warn("[{}] No values in the response", sessionContext.getDeviceId()); throw new IllegalArgumentException("No values in the response"); } @@ -428,11 +464,11 @@ public class SnmpTransportService implements TbTransportService, CommandResponde @PreDestroy public void shutdown() { log.info("Stopping SNMP transport!"); - if (queryingExecutor != null) { - queryingExecutor.shutdownNow(); + if (scheduler != null) { + scheduler.shutdownNow(); } - if (responseProcessingExecutor != null) { - responseProcessingExecutor.shutdownNow(); + if (executor != null) { + executor.shutdownNow(); } if (snmp != null) { try { diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java index 5819c76f74..8ec0c6841d 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java @@ -45,7 +45,6 @@ import org.thingsboard.server.transport.snmp.SnmpTransportContext; import java.util.LinkedList; import java.util.List; import java.util.UUID; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; @Slf4j @@ -60,7 +59,8 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @Setter private SnmpDeviceTransportConfiguration deviceTransportConfiguration; @Getter - private final Device device; + @Setter + private Device device; @Getter private final TenantId tenantId; @@ -73,7 +73,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S private Runnable sessionTimeoutHandler; @Getter - private final List> queryingTasks = new LinkedList<>(); + private final List queryingTasks = new LinkedList<>(); @Builder public DeviceSessionContext(TenantId tenantId, Device device, DeviceProfile deviceProfile, String token, diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/ScheduledTask.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/ScheduledTask.java new file mode 100644 index 0000000000..43d83e5e2c --- /dev/null +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/ScheduledTask.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.transport.snmp.session; + +import com.google.common.util.concurrent.AsyncCallable; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Data +@Slf4j +public class ScheduledTask { + private ListenableFuture scheduledFuture; + private boolean stopped = false; + + public void init(AsyncCallable task, long delayMs, ScheduledExecutorService scheduler) { + schedule(task, delayMs, scheduler); + } + + private void schedule(AsyncCallable task, long delayMs, ScheduledExecutorService scheduler) { + scheduledFuture = Futures.scheduleAsync(() -> { + if (stopped) { + return Futures.immediateCancelledFuture(); + } + try { + return task.call(); + } catch (Throwable t) { + log.error("Unhandled error in scheduled task", t); + return Futures.immediateFailedFuture(t); + } + }, delayMs, TimeUnit.MILLISECONDS, scheduler); + if (!stopped) { + scheduledFuture.addListener(() -> schedule(task, delayMs, scheduler), MoreExecutors.directExecutor()); + } + } + + public void cancel() { + stopped = true; + if (scheduledFuture != null) { + scheduledFuture.cancel(true); + } + } + +} diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java index 261e67bac8..ced8f21288 100644 --- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java +++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java @@ -58,11 +58,10 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { private final Target target; private final Address address; + private final Map mappings; private Snmp snmp; - private final String password; - - public SnmpDeviceSimulatorV2(int port, String password) throws IOException { + public SnmpDeviceSimulatorV2(int port, String password, Map mappings) throws IOException { super(new File("conf.agent"), new File("bootCounter.agent"), new CommandProcessor(new OctetString("12312"))); CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString(password)); @@ -72,7 +71,7 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { target.setTimeout(1500); target.setVersion(SnmpConstants.version2c); this.target = target; - this.password = password; + this.mappings = mappings; } public void start() throws IOException { @@ -85,13 +84,6 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { snmp = new Snmp(transportMappings[0]); } - public void setUpMappings(Map oidToResponseMappings) { - unregisterManagedObject(getSnmpv2MIB()); - oidToResponseMappings.forEach((oid, response) -> { - registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response))); - }); - } - public void sendTrap(String host, int port, Map values) throws IOException { PDU pdu = new PDU(); pdu.addAll(values.entrySet().stream() @@ -107,6 +99,10 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { @Override protected void registerManagedObjects() { + unregisterManagedObject(getSnmpv2MIB()); + mappings.forEach((oid, response) -> { + registerManagedObject(new MOScalar<>(new OID(oid), MOAccessImpl.ACCESS_READ_WRITE, new OctetString(response))); + }); } protected void registerManagedObject(ManagedObject mo) { @@ -152,6 +148,7 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { } protected void unregisterManagedObjects() { + unregisterManagedObject(getSnmpv2MIB()); } protected void addCommunities(SnmpCommunityMIB communityMIB) { diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java index cb99c85655..c87507f962 100644 --- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java +++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java @@ -15,28 +15,34 @@ */ package org.thingsboard.server.transport.snmp; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.thingsboard.common.util.JacksonUtil; + +import java.io.File; import java.io.IOException; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Scanner; +import java.util.stream.Collectors; public class SnmpTestV2 { private static final Scanner scanner = new Scanner(System.in); public static void main(String[] args) throws IOException { - SnmpDeviceSimulatorV2 client = new SnmpDeviceSimulatorV2(1610, "public"); + Map mappings = new LinkedHashMap<>(); + for (int i = 1; i <= 50; i++) { + String oid = String.format("1.3.6.1.2.1.%s.1.52", i); + mappings.put(oid, "value_" + i); + } - client.start(); - Map mappings = new HashMap<>(); -// for (int i = 1; i <= 500; i++) { -// String oid = String.format(".1.3.6.1.2.1.%s.1.52", i); -// mappings.put(oid, "value_" + i); -// } - mappings.put("1.3.6.1.2.1.266.1.52", "****"); + SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public", mappings); + device.start(); - client.setUpMappings(mappings); - inputTraps(client); + System.out.println("Hosting the following values:\n" + mappings.entrySet().stream() + .map(entry -> entry.getKey() + " - " + entry.getValue()) + .collect(Collectors.joining("\n"))); scanner.nextLine(); } @@ -53,4 +59,18 @@ public class SnmpTestV2 { } } + private static void updateDeviceProfile(String file) throws Exception { + File profileFile = new File(file); + JsonNode deviceProfile = JacksonUtil.OBJECT_MAPPER.readTree(profileFile); + ArrayNode mappingsJson = (ArrayNode) deviceProfile.at("/profileData/transportConfiguration/communicationConfigs/0/mappings"); + for (int i = 1; i <= 50; i++) { + String oid = String.format(".1.3.6.1.2.1.%s.1.52", i); + mappingsJson.add(JacksonUtil.newObjectNode() + .put("oid", oid) + .put("key", "key_" + i) + .put("dataType", "STRING")); + } + JacksonUtil.OBJECT_MAPPER.writeValue(profileFile, deviceProfile); + } + } diff --git a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json deleted file mode 100644 index f74ebca0bf..0000000000 --- a/common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "timeoutMs": 500, - "retries": 0, - "communicationConfigs": [ - { - "spec": "TELEMETRY_QUERYING", - "queryingFrequencyMs": 3000, - "mappings": [ - { - "oid": ".1.3.6.1.2.1.1.1.50", - "key": "temperature", - "dataType": "LONG" - }, - { - "oid": ".1.3.6.1.2.1.2.1.52", - "key": "humidity", - "dataType": "DOUBLE" - } - ] - }, - { - "spec": "CLIENT_ATTRIBUTES_QUERYING", - "queryingFrequencyMs": 5000, - "mappings": [ - { - "oid": ".1.3.6.1.2.1.3.1.54", - "key": "isCool", - "dataType": "STRING" - } - ] - }, - { - "spec": "SHARED_ATTRIBUTES_SETTING", - "mappings": [ - { - "oid": ".1.3.6.1.2.1.7.1.58", - "key": "shared", - "dataType": "STRING" - } - ] - } - ] -} diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json deleted file mode 100644 index 039e03fa53..0000000000 --- a/common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "address": "192.168.3.23", - "port": 1610, - "protocolVersion": "V3", - - "username": "tb-user", - "engineId": "qwertyuioa", - "securityName": "tb-user", - "authenticationProtocol": "SHA_512", - "authenticationPassphrase": "sdfghjkloifgh", - "privacyProtocol": "DES", - "privacyPassphrase": "rtytguijokod" -} \ No newline at end of file diff --git a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json b/common/transport/snmp/src/test/resources/snmp-device-transport-config.json deleted file mode 100644 index c73d817bfb..0000000000 --- a/common/transport/snmp/src/test/resources/snmp-device-transport-config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "address": "127.0.0.1", - "port": 1610, - "community": "public", - "protocolVersion": "V2C" -} \ No newline at end of file diff --git a/common/transport/snmp/src/test/resources/snmp_device_profile.json b/common/transport/snmp/src/test/resources/snmp_device_profile.json new file mode 100644 index 0000000000..0e72c46ce8 --- /dev/null +++ b/common/transport/snmp/src/test/resources/snmp_device_profile.json @@ -0,0 +1,289 @@ +{ + "name": "SNMP Device Profile", + "description": "", + "image": null, + "type": "DEFAULT", + "transportType": "SNMP", + "provisionType": "DISABLED", + "defaultRuleChainId": null, + "defaultDashboardId": null, + "defaultQueueName": null, + "profileData": { + "configuration": { + "type": "DEFAULT" + }, + "transportConfiguration": { + "type": "SNMP", + "timeoutMs": 500, + "retries": 0, + "communicationConfigs": [ + { + "spec": "TELEMETRY_QUERYING", + "mappings": [ + { + "oid": ".1.3.6.1.2.1.1.1.52", + "key": "key_1", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.2.1.52", + "key": "key_2", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.3.1.52", + "key": "key_3", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.4.1.52", + "key": "key_4", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.5.1.52", + "key": "key_5", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.6.1.52", + "key": "key_6", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.7.1.52", + "key": "key_7", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.8.1.52", + "key": "key_8", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.9.1.52", + "key": "key_9", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.10.1.52", + "key": "key_10", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.11.1.52", + "key": "key_11", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.12.1.52", + "key": "key_12", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.13.1.52", + "key": "key_13", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.14.1.52", + "key": "key_14", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.15.1.52", + "key": "key_15", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.16.1.52", + "key": "key_16", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.17.1.52", + "key": "key_17", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.18.1.52", + "key": "key_18", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.19.1.52", + "key": "key_19", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.20.1.52", + "key": "key_20", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.21.1.52", + "key": "key_21", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.22.1.52", + "key": "key_22", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.23.1.52", + "key": "key_23", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.24.1.52", + "key": "key_24", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.25.1.52", + "key": "key_25", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.26.1.52", + "key": "key_26", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.27.1.52", + "key": "key_27", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.28.1.52", + "key": "key_28", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.29.1.52", + "key": "key_29", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.30.1.52", + "key": "key_30", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.31.1.52", + "key": "key_31", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.32.1.52", + "key": "key_32", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.33.1.52", + "key": "key_33", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.34.1.52", + "key": "key_34", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.35.1.52", + "key": "key_35", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.36.1.52", + "key": "key_36", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.37.1.52", + "key": "key_37", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.38.1.52", + "key": "key_38", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.39.1.52", + "key": "key_39", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.40.1.52", + "key": "key_40", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.41.1.52", + "key": "key_41", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.42.1.52", + "key": "key_42", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.43.1.52", + "key": "key_43", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.44.1.52", + "key": "key_44", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.45.1.52", + "key": "key_45", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.46.1.52", + "key": "key_46", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.47.1.52", + "key": "key_47", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.48.1.52", + "key": "key_48", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.49.1.52", + "key": "key_49", + "dataType": "STRING" + }, + { + "oid": ".1.3.6.1.2.1.50.1.52", + "key": "key_50", + "dataType": "STRING" + } + ], + "queryingFrequencyMs": 5000 + } + ] + }, + "provisionConfiguration": { + "type": "DISABLED", + "provisionDeviceSecret": null + }, + "alarms": null + }, + "provisionDeviceKey": null, + "firmwareId": null, + "softwareId": null, + "defaultEdgeRuleChainId": null, + "default": false +} \ No newline at end of file diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 0d7fbfc332..4aa989ed25 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -788,7 +788,11 @@ public class DefaultTransportService extends TransportActivityManager implements .setSuccess(success) .setError(error != null ? ExceptionUtils.getStackTrace(error) : "")) .build(); - sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + try { + sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + } catch (Exception e) { + log.error("[{}][{}] Failed to send lifecycle event to core", tenantId, deviceId, e); + } } @Override @@ -801,9 +805,13 @@ public class DefaultTransportService extends TransportActivityManager implements .setEntityIdLSB(deviceId.getId().getLeastSignificantBits()) .setServiceId(serviceInfoProvider.getServiceId()) .setMethod(method) - .setError(ExceptionUtils.getStackTrace(error))) + .setError(ExceptionUtils.getRootCauseMessage(error))) .build(); - sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + try { + sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + } catch (Exception e) { + log.error("[{}][{}] Failed to send error event to core", tenantId, deviceId, e); + } } @Override @@ -985,10 +993,10 @@ public class DefaultTransportService extends TransportActivityManager implements log.warn("ResourceDelete - [{}] [{}]", id, mdRez); transportCallbackExecutor.submit(() -> mdRez.getListener().onResourceDelete(msg)); }); - } else if (toSessionMsg.hasQueueUpdateMsg()) { - partitionService.updateQueue(toSessionMsg.getQueueUpdateMsg()); - } else if (toSessionMsg.hasQueueDeleteMsg()) { - partitionService.removeQueue(toSessionMsg.getQueueDeleteMsg()); + } else if (toSessionMsg.getQueueUpdateMsgsCount() > 0) { + partitionService.updateQueues(toSessionMsg.getQueueUpdateMsgsList()); + } else if (toSessionMsg.getQueueDeleteMsgsCount() > 0) { + partitionService.removeQueues(toSessionMsg.getQueueDeleteMsgsList()); } else { //TODO: should we notify the device actor about missed session? log.debug("[{}] Missing session.", sessionId); diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java index 60e75b5907..1d6688cebc 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java @@ -160,7 +160,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe @Override protected void onTbApplicationEvent(PartitionChangeEvent event) { for (TenantId tenantId : vcService.getActiveRepositoryTenants()) { - if (!partitionService.resolve(ServiceType.TB_VC_EXECUTOR, tenantId, tenantId).isMyPartition()) { + if (!partitionService.isMyPartition(ServiceType.TB_VC_EXECUTOR, tenantId, tenantId)) { var lock = getRepoLock(tenantId); lock.lock(); try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java index b1395ee34b..91b4f339b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.alarm; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; -import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -29,13 +28,10 @@ import java.util.UUID; public interface AlarmCommentDao extends Dao { - AlarmComment createAlarmComment(TenantId tenantId, AlarmComment alarmComment); - - void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId); - AlarmComment findAlarmCommentById(TenantId tenantId, UUID key); PageData findAlarmComments(TenantId tenantId, AlarmId id, PageLink pageLink); ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, UUID key); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java index 5bdd9803b2..ac6f4955d8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.alarm; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; @@ -35,8 +34,6 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.service.DataValidator; -import java.util.UUID; - import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -101,12 +98,7 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al if (alarmComment.getType() == null) { alarmComment.setType(AlarmCommentType.OTHER); } - if (alarmComment.getId() == null) { - UUID uuid = Uuids.timeBased(); - alarmComment.setId(new AlarmCommentId(uuid)); - alarmComment.setCreatedTime(Uuids.unixTimestamp(uuid)); - } - return alarmCommentDao.createAlarmComment(tenantId, alarmComment); + return alarmCommentDao.save(tenantId, alarmComment); } private AlarmComment updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java index ba4b4283e7..ad5a4ba686 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java @@ -30,8 +30,6 @@ import java.util.UUID; public interface AuditLogDao extends Dao { - ListenableFuture saveByTenantId(AuditLog auditLog); - PageData findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, List actionTypes, TimePageLink pageLink); PageData findAuditLogsByTenantIdAndCustomerId(UUID tenantId, CustomerId customerId, List actionTypes, TimePageLink pageLink); @@ -42,6 +40,4 @@ public interface AuditLogDao extends Dao { void cleanUpAuditLogs(long expTime); - void migrateAuditLogs(); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 3252a481c4..4350f2a5d1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -15,11 +15,9 @@ */ package org.thingsboard.server.dao.audit; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -34,7 +32,6 @@ import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.audit.ActionStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.AuditLogId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -50,11 +47,11 @@ import org.thingsboard.server.dao.audit.sink.AuditLogSink; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.sql.JpaExecutorService; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; import static org.thingsboard.server.dao.service.Validator.validateEntityId; @@ -66,7 +63,6 @@ import static org.thingsboard.server.dao.service.Validator.validateId; public class AuditLogServiceImpl implements AuditLogService { private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - private static final int INSERTS_PER_ENTRY = 3; @Autowired private AuditLogLevelFilter auditLogLevelFilter; @@ -80,6 +76,9 @@ public class AuditLogServiceImpl implements AuditLogService { @Autowired private AuditLogSink auditLogSink; + @Autowired + private JpaExecutorService executor; + @Autowired private DataValidator auditLogValidator; @@ -115,7 +114,7 @@ public class AuditLogServiceImpl implements AuditLogService { } @Override - public ListenableFuture> + public ListenableFuture logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { if (canLog(entityId.getEntityType(), actionType)) { @@ -370,9 +369,6 @@ public class AuditLogServiceImpl implements AuditLogService { ActionStatus actionStatus, String actionFailureDetails) { AuditLog result = new AuditLog(); - UUID id = Uuids.timeBased(); - result.setId(new AuditLogId(id)); - result.setCreatedTime(Uuids.unixTimestamp(id)); result.setTenantId(tenantId); result.setEntityId(entityId); result.setEntityName(entityName); @@ -386,16 +382,16 @@ public class AuditLogServiceImpl implements AuditLogService { return result; } - private ListenableFuture> logAction(TenantId tenantId, - EntityId entityId, - String entityName, - CustomerId customerId, - UserId userId, - String userName, - ActionType actionType, - JsonNode actionData, - ActionStatus actionStatus, - String actionFailureDetails) { + private ListenableFuture logAction(TenantId tenantId, + EntityId entityId, + String entityName, + CustomerId customerId, + UserId userId, + String userName, + ActionType actionType, + JsonNode actionData, + ActionStatus actionStatus, + String actionFailureDetails) { AuditLog auditLogEntry = createAuditLogEntry(tenantId, entityId, entityName, customerId, userId, userName, actionType, actionData, actionStatus, actionFailureDetails); log.trace("Executing logAction [{}]", auditLogEntry); @@ -408,12 +404,12 @@ public class AuditLogServiceImpl implements AuditLogService { return Futures.immediateFailedFuture(e); } } - List> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY); - futures.add(auditLogDao.saveByTenantId(auditLogEntry)); - auditLogSink.logAction(auditLogEntry); - - return Futures.allAsList(futures); + return executor.submit(() -> { + AuditLog auditLog = auditLogDao.save(tenantId, auditLogEntry); + auditLogSink.logAction(auditLog); + return null; + }); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java index 87ede9452a..28c00e4d58 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java @@ -55,7 +55,7 @@ public class DummyAuditLogServiceImpl implements AuditLogService { } @Override - public ListenableFuture> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { + public ListenableFuture logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { return null; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java b/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java index 18e24b9094..f2dc272848 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java @@ -34,14 +34,18 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.id.TenantId; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; @Component @ConditionalOnProperty(prefix = "audit-log.sink", value = "type", havingValue = "elasticsearch") @@ -68,6 +72,7 @@ public class ElasticsearchAuditLogSink implements AuditLogSink { private String dateFormat; private RestClient restClient; + private ExecutorService executor; @PostConstruct public void init() { @@ -87,14 +92,32 @@ public class ElasticsearchAuditLogSink implements AuditLogSink { } this.restClient = builder.build(); + this.executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("elasticsearch-audit-log")); } catch (Exception e) { log.error("Sink init failed!", e); throw new RuntimeException(e.getMessage(), e); } } + @PreDestroy + private void destroy() { + if (executor != null) { + executor.shutdownNow(); + } + } + @Override public void logAction(AuditLog auditLogEntry) { + executor.execute(() -> { + try { + doLogAction(auditLogEntry); + } catch (Exception e) { + log.error("Failed to log action", e); + } + }); + } + + private void doLogAction(AuditLog auditLogEntry) { String jsonContent = createElasticJsonRecord(auditLogEntry); HttpEntity entity = new NStringEntity( diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index f190923167..2f7fe0680c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -157,16 +157,19 @@ public class DeviceServiceImpl extends AbstractCachedEntityService deviceDao.findDeviceByTenantIdAndName(tenantId.getId(), name).orElse(null), true); } + @Transactional @Override public Device saveDeviceWithAccessToken(Device device, String accessToken) { return doSaveDevice(device, accessToken, true); } + @Transactional @Override public Device saveDevice(Device device, boolean doValidate) { return doSaveDevice(device, null, doValidate); } + @Transactional @Override public Device saveDevice(Device device) { return doSaveDevice(device, null, true); diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java index 9ea70f6c5a..4d1e126c49 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java @@ -19,11 +19,15 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.limits.RateLimitService; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.dao.service.DataValidator; @Service @@ -32,11 +36,17 @@ import org.thingsboard.server.dao.service.DataValidator; public class BaseEdgeEventService implements EdgeEventService { private final EdgeEventDao edgeEventDao; - + private final RateLimitService rateLimitService; private final DataValidator edgeEventValidator; @Override public ListenableFuture saveAsync(EdgeEvent edgeEvent) { + if (!rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS, edgeEvent.getTenantId())) { + throw new TbRateLimitsException(EntityType.TENANT); + } + if (!rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS_PER_EDGE, edgeEvent.getTenantId(), edgeEvent.getEdgeId())) { + throw new TbRateLimitsException(EntityType.EDGE); + } edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId); return edgeEventDao.saveAsync(edgeEvent); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 80eea0a478..136fd0485d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -30,10 +30,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; @@ -48,14 +48,15 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; @@ -64,7 +65,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; -import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; @@ -105,7 +106,10 @@ public class EdgeServiceImpl extends AbstractCachedEntityService edgeValidator; @@ -113,6 +117,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService isEdgeActiveAsync(TenantId tenantId, EdgeId edgeId, String key) { + ListenableFuture> futureKvEntry; + if (persistToTelemetry) { + futureKvEntry = timeseriesService.findLatest(tenantId, edgeId, key); + } else { + futureKvEntry = attributesService.find(tenantId, edgeId, DataConstants.SERVER_SCOPE, key); + } + return Futures.transformAsync(futureKvEntry, kvEntryOpt -> + Futures.immediateFuture(kvEntryOpt.flatMap(KvEntry::getBooleanValue).orElse(false)), MoreExecutors.directExecutor()); + } + private List findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { List result = new ArrayList<>(); PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); diff --git a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java index b9d7f7df86..88834eecbc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java @@ -130,11 +130,6 @@ public class BaseEventService implements EventService { eventDao.cleanupEvents(regularEventExpTs, debugEventExpTs, cleanupDb); } - @Override - public void migrateEvents() { - eventDao.migrateEvents(ttlInSec > 0 ? (System.currentTimeMillis() - ttlInSec * 1000) : 0, debugTtlInSec > 0 ? (System.currentTimeMillis() - debugTtlInSec * 1000) : 0); - } - private PageData convert(EntityType entityType, PageData pd) { return new PageData<>(pd.getData() == null ? null : pd.getData().stream().map(e -> e.toInfo(entityType)).collect(Collectors.toList()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java index 23d4245575..e569745504 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java @@ -27,7 +27,7 @@ import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import javax.annotation.PreDestroy; diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java index 822768c572..59fdcac935 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java @@ -27,7 +27,7 @@ import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import javax.annotation.PreDestroy; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 7a6c420271..1bdc835b29 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.notification.targets.platform.SystemAd import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettingsType; @@ -54,6 +55,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -197,6 +199,8 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.alarmComment, tenantAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.alarmAssignment, affectedUser.getId()); defaultNotifications.create(tenantId, DefaultNotifications.ruleEngineComponentLifecycleFailure, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.edgeConnection, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.edgeCommunicationFailures, tenantAdmins.getId()); } @Override @@ -208,17 +212,43 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS } NotificationTarget sysAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.SYSTEM_ADMINISTRATORS).stream() - .findFirst().orElseGet(() -> { - return createTarget(tenantId, "System administrators", new SystemAdministratorsFilter(), "All system administrators"); - }); + .findFirst().orElseGet(() -> createTarget(tenantId, "System administrators", new SystemAdministratorsFilter(), "All system administrators")); NotificationTarget affectedTenantAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.AFFECTED_TENANT_ADMINISTRATORS).stream() - .findFirst().orElseGet(() -> { - return createTarget(tenantId, "Affected tenant's administrators", new AffectedTenantAdministratorsFilter(), ""); - }); + .findFirst().orElseGet(() -> createTarget(tenantId, "Affected tenant's administrators", new AffectedTenantAdministratorsFilter(), "")); defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); + } else { + var requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE); + var existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes( + tenantId, requiredNotificationTypes, new PageLink(1)) + .getData() + .stream() + .map(NotificationTemplate::getNotificationType) + .collect(Collectors.toSet()); + + if (existingNotificationTypes.containsAll(requiredNotificationTypes)) { + return; + } + + NotificationTarget tenantAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.TENANT_ADMINISTRATORS) + .stream() + .findFirst() + .orElseGet(() -> createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(), "Tenant administrators")); + + for (NotificationType type : requiredNotificationTypes) { + if (!existingNotificationTypes.contains(type)) { + switch (type) { + case EDGE_CONNECTION: + defaultNotifications.create(tenantId, DefaultNotifications.edgeConnection, tenantAdmins.getId()); + break; + case EDGE_COMMUNICATION_FAILURE: + defaultNotifications.create(tenantId, DefaultNotifications.edgeCommunicationFailures, tenantAdmins.getId()); + break; + } + } + } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 5a78fa9864..fd17618bd1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -40,6 +40,9 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Alarm import org.thingsboard.server.common.data.notification.rule.trigger.config.ApiUsageLimitNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig.EdgeConnectivityEvent; import org.thingsboard.server.common.data.notification.rule.trigger.config.EntitiesLimitNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig; @@ -325,6 +328,35 @@ public class DefaultNotifications { .description("Send notification to tenant admins when any Rule chain or Rule node failed to start, update or stop") .build()) .build(); + public static final DefaultNotification edgeConnection = DefaultNotification.builder() + .name("Edge connection notification") + .type(NotificationType.EDGE_CONNECTION) + .subject("Edge connection status change") + .text("Edge '${edgeName}' is now ${eventType}") + .icon("info").color(null) + .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") + .rule(DefaultRule.builder() + .name("Edge connection status change") + .triggerConfig(EdgeConnectionNotificationRuleTriggerConfig.builder() + .edges(null) + .notifyOn(Set.of(EdgeConnectivityEvent.CONNECTED, EdgeConnectivityEvent.DISCONNECTED)) + .build()) + .description("Send notification to tenant admins when the connection status between TB and Edge changes") + .build()) + .build(); + public static final DefaultNotification edgeCommunicationFailures = DefaultNotification.builder() + .name("Edge communication failure notification") + .type(NotificationType.EDGE_COMMUNICATION_FAILURE) + .subject("Edge '${edgeName}' communication failure occurred") + .text("Failure message: '${failureMsg}'") + .icon("error").color(RED_COLOR) + .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") + .rule(DefaultRule.builder() + .name("Edge communication failure") + .triggerConfig(EdgeCommunicationFailureNotificationRuleTriggerConfig.builder().edges(null).build()) + .description("Send notification to tenant admins when communication failures occur") + .build()) + .build(); public static final DefaultNotification jwtSigningKeyIssue = DefaultNotification.builder() .name("JWT Signing Key issue notification") @@ -346,7 +378,7 @@ public class DefaultNotifications { if (defaultNotification.getRule() != null && targets.length > 0) { NotificationRule rule = defaultNotification.toRule(template.getId(), targets); rule.setTenantId(tenantId); - rule = ruleService.saveNotificationRule(tenantId, rule); + ruleService.saveNotificationRule(tenantId, rule); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java index 57c45b1d0b..0755fb1526 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java @@ -57,9 +57,6 @@ public class BaseQueueService extends AbstractEntityService implements QueueServ @Autowired private DataValidator queueValidator; -// @Autowired -// private QueueStatsService queueStatsService; - @Override public Queue saveQueue(Queue queue) { log.trace("Executing createOrUpdateQueue [{}]", queue); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 1d1296c42e..962e0429df 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -149,6 +149,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } @Override + @Transactional public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function ruleNodeUpdater) { Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id."); RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 3d3dbd9bbc..35a161b94d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -27,6 +27,8 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.util.SqlDao; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -45,9 +47,6 @@ public abstract class JpaAbstractDao, D> protected abstract JpaRepository getRepository(); - protected void setSearchText(E entity) { - } - @Override @Transactional public D save(TenantId tenantId, D domain) { @@ -58,17 +57,21 @@ public abstract class JpaAbstractDao, D> log.error("Can't create entity for domain object {}", domain, e); throw new IllegalArgumentException("Can't create entity for domain object {" + domain + "}", e); } - setSearchText(entity); log.debug("Saving entity {}", entity); - if (entity.getUuid() == null) { + boolean isNew = entity.getUuid() == null; + if (isNew) { UUID uuid = Uuids.timeBased(); entity.setUuid(uuid); entity.setCreatedTime(Uuids.unixTimestamp(uuid)); } - entity = getRepository().save(entity); + entity = doSave(entity, isNew); return DaoUtil.getData(entity); } + protected E doSave(E entity, boolean isNew) { + return getRepository().save(entity); + } + @Override @Transactional public D saveAndFlush(TenantId tenantId, D domain) { @@ -121,4 +124,5 @@ public abstract class JpaAbstractDao, D> List entities = Lists.newArrayList(getRepository().findAll()); return DaoUtil.convertDataList(entities); } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java new file mode 100644 index 0000000000..a933837f5d --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.dao.sql; + +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.util.SqlDao; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; + +@SqlDao +public abstract class JpaPartitionedAbstractDao, D> extends JpaAbstractDao { + + @PersistenceContext + private EntityManager entityManager; + + @Override + protected E doSave(E entity, boolean isNew) { + createPartition(entity); + if (isNew) { + entityManager.persist(entity); + } else { + entity = entityManager.merge(entity); + } + return entity; + } + + public abstract void createPartition(E entity); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java index 1f83211b79..3d366a5a56 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java @@ -24,7 +24,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; -import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -32,7 +31,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmCommentDao; import org.thingsboard.server.dao.model.sql.AlarmCommentEntity; -import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.sql.JpaPartitionedAbstractDao; import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import org.thingsboard.server.dao.util.SqlDao; @@ -45,7 +44,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_TABL @Component @SqlDao @RequiredArgsConstructor -public class JpaAlarmCommentDao extends JpaAbstractDao implements AlarmCommentDao { +public class JpaAlarmCommentDao extends JpaPartitionedAbstractDao implements AlarmCommentDao { private final SqlPartitioningRepository partitioningRepository; @Value("${sql.alarm_comments.partition_size:168}") private int partitionSizeInHours; @@ -54,21 +53,7 @@ public class JpaAlarmCommentDao extends JpaAbstractDao findAlarmComments(TenantId tenantId, AlarmId id, PageLink pageLink){ + public PageData findAlarmComments(TenantId tenantId, AlarmId id, PageLink pageLink) { log.trace("Try to find alarm comments by alarm id using [{}]", id); return DaoUtil.toPageData( alarmCommentRepository.findAllByAlarmId(id.getId(), DaoUtil.toPageable(pageLink))); @@ -86,6 +71,11 @@ public class JpaAlarmCommentDao extends JpaAbstractDao getEntityClass() { return AlarmCommentEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java index f78dc339dc..f73cb95c27 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.dao.sql.audit; -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -25,10 +23,8 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; -import org.thingsboard.server.common.data.id.AuditLogId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; @@ -36,12 +32,11 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.audit.AuditLogDao; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sql.AuditLogEntity; -import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.sql.JpaPartitionedAbstractDao; import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -49,7 +44,7 @@ import java.util.concurrent.TimeUnit; @SqlDao @RequiredArgsConstructor @Slf4j -public class JpaAuditLogDao extends JpaAbstractDao implements AuditLogDao { +public class JpaAuditLogDao extends JpaPartitionedAbstractDao implements AuditLogDao { private final AuditLogRepository auditLogRepository; private final SqlPartitioningRepository partitioningRepository; @@ -72,25 +67,6 @@ public class JpaAuditLogDao extends JpaAbstractDao imp return auditLogRepository; } - @Override - public ListenableFuture saveByTenantId(AuditLog auditLog) { - return service.submit(() -> { - save(auditLog.getTenantId(), auditLog); - return null; - }); - } - - @Override - public AuditLog save(TenantId tenantId, AuditLog auditLog) { - if (auditLog.getId() == null) { - UUID uuid = Uuids.timeBased(); - auditLog.setId(new AuditLogId(uuid)); - auditLog.setCreatedTime(Uuids.unixTimestamp(uuid)); - } - partitioningRepository.createPartitionIfNotExists(TABLE_NAME, auditLog.getCreatedTime(), TimeUnit.HOURS.toMillis(partitionSizeInHours)); - return super.save(tenantId, auditLog); - } - @Override public PageData findAuditLogsByTenantIdAndEntityId(UUID tenantId, EntityId entityId, List actionTypes, TimePageLink pageLink) { return DaoUtil.toPageData( @@ -152,34 +128,8 @@ public class JpaAuditLogDao extends JpaAbstractDao imp } @Override - public void migrateAuditLogs() { - long startTime = ttlInSec > 0 ? System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(ttlInSec) : 1480982400000L; - - long currentTime = System.currentTimeMillis(); - var partitionStepInMs = TimeUnit.HOURS.toMillis(partitionSizeInHours); - long numberOfPartitions = (currentTime - startTime) / partitionStepInMs; - - if (numberOfPartitions > 1000) { - String error = "Please adjust your audit logs partitioning configuration. Configuration with partition size " + - "of " + partitionSizeInHours + " hours and corresponding TTL will use " + numberOfPartitions + " " + - "(> 1000) partitions which is not recommended!"; - log.error(error); - throw new RuntimeException(error); - } - - while (startTime < currentTime) { - var endTime = startTime + partitionStepInMs; - log.info("Migrating audit logs for time period: {} - {}", startTime, endTime); - callMigrationFunction(startTime, endTime, partitionStepInMs); - startTime = endTime; - } - log.info("Audit logs migration finished"); - - jdbcTemplate.execute("DROP TABLE IF EXISTS old_audit_log"); - } - - private void callMigrationFunction(long startTime, long endTime, long partitionSizeInMs) { - jdbcTemplate.update("CALL migrate_audit_logs(?, ?, ?)", startTime, endTime, partitionSizeInMs); + public void createPartition(AuditLogEntity entity) { + partitioningRepository.createPartitionIfNotExists(TABLE_NAME, entity.getCreatedTime(), TimeUnit.HOURS.toMillis(partitionSizeInHours)); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java index f5ecaaab84..3e8e8ebf54 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java @@ -35,7 +35,7 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.edge.EdgeEventDao; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.sql.EdgeEventEntity; -import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.sql.JpaPartitionedAbstractDao; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; @@ -47,7 +47,6 @@ import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -58,7 +57,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @SqlDao @RequiredArgsConstructor @Slf4j -public class JpaBaseEdgeEventDao extends JpaAbstractDao implements EdgeEventDao { +public class JpaBaseEdgeEventDao extends JpaPartitionedAbstractDao implements EdgeEventDao { private final UUID systemTenantId = NULL_UUID; @@ -151,8 +150,9 @@ public class JpaBaseEdgeEventDao extends JpaAbstractDao save(EdgeEventEntity entity) { @@ -227,4 +227,10 @@ public class JpaBaseEdgeEventDao extends JpaAbstractDao implements NotificationDao { +public class JpaNotificationDao extends JpaPartitionedAbstractDao implements NotificationDao { private final NotificationRepository notificationRepository; private final SqlPartitioningRepository partitioningRepository; @@ -52,18 +50,6 @@ public class JpaNotificationDao extends JpaAbstractDao findUnreadByRecipientIdAndPageLink(TenantId tenantId, UserId recipientId, PageLink pageLink) { return DaoUtil.toPageData(notificationRepository.findByRecipientIdAndStatusNot(recipientId.getId(), NotificationStatus.READ, @@ -114,6 +100,12 @@ public class JpaNotificationDao extends JpaAbstractDao getEntityClass() { return NotificationEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java index 9236d0fbb0..10833b11df 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -58,6 +59,7 @@ public class JpaOtaPackageInfoDao extends JpaAbstractDao FIXED_PARTITION = List.of(0L); + protected static final String INSERT_WITH_NULL = INSERT_INTO + ModelConstants.TS_KV_CF + + "(" + ModelConstants.ENTITY_TYPE_COLUMN + + "," + ModelConstants.ENTITY_ID_COLUMN + + "," + ModelConstants.KEY_COLUMN + + "," + ModelConstants.PARTITION_COLUMN + + "," + ModelConstants.TS_COLUMN + + "," + ModelConstants.BOOLEAN_VALUE_COLUMN + + "," + ModelConstants.STRING_VALUE_COLUMN + + "," + ModelConstants.LONG_VALUE_COLUMN + + "," + ModelConstants.DOUBLE_VALUE_COLUMN + + "," + ModelConstants.JSON_VALUE_COLUMN + ")" + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private CassandraTsPartitionsCache cassandraTsPartitionsCache; @@ -117,6 +130,8 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD private PreparedStatement[] fetchStmtsAsc; private PreparedStatement[] fetchStmtsDesc; private PreparedStatement deleteStmt; + private PreparedStatement saveWithNullStmt; + private PreparedStatement saveWithNullWithTtlStmt; private final Lock stmtCreationLock = new ReentrantLock(); private boolean isInstall() { @@ -159,19 +174,36 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD ttl = computeTtl(ttl); int dataPointDays = tsKvEntry.getDataPoints() * Math.max(1, (int) (ttl / SECONDS_IN_DAY)); long partition = toPartitionTs(tsKvEntry.getTs()); + String entityType = entityId.getEntityType().name(); + UUID entityIdId = entityId.getId(); + String entryKey = tsKvEntry.getKey(); + long ts = tsKvEntry.getTs(); DataType type = tsKvEntry.getDataType(); + BoundStatementBuilder stmtBuilder; if (setNullValuesEnabled) { - processSetNullValues(tenantId, entityId, tsKvEntry, ttl, futures, partition, type); - } - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind()); - stmtBuilder.setString(0, entityId.getEntityType().name()) - .setUuid(1, entityId.getId()) - .setString(2, tsKvEntry.getKey()) - .setLong(3, partition) - .setLong(4, tsKvEntry.getTs()); - addValue(tsKvEntry, stmtBuilder, 5); - if (ttl > 0) { - stmtBuilder.setInt(6, (int) ttl); + Boolean booleanValue = tsKvEntry.getBooleanValue().orElse(null); + String strValue = tsKvEntry.getStrValue().orElse(null); + Long longValue = tsKvEntry.getLongValue().orElse(null); + Double doubleValue = tsKvEntry.getDoubleValue().orElse(null); + String jsonValue = tsKvEntry.getJsonValue().orElse(null); + if (ttl == 0) { + stmtBuilder = new BoundStatementBuilder(getSaveWithNullStmt() + .bind(entityType, entityIdId, entryKey, partition, ts, booleanValue, strValue, longValue, doubleValue, jsonValue)); + } else { + stmtBuilder = new BoundStatementBuilder(getSaveWithNullWithTtlStmt() + .bind(entityType, entityIdId, entryKey, partition, ts, booleanValue, strValue, longValue, doubleValue, jsonValue, (int) ttl)); + } + } else { + stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind()); + stmtBuilder.setString(0, entityType) + .setUuid(1, entityIdId) + .setString(2, entryKey) + .setLong(3, partition) + .setLong(4, ts); + addValue(tsKvEntry, stmtBuilder, 5); + if (ttl > 0) { + stmtBuilder.setInt(6, (int) ttl); + } } BoundStatement stmt = stmtBuilder.build(); futures.add(getFuture(executeAsyncWrite(tenantId, stmt), rs -> null)); @@ -449,56 +481,6 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD return tsFormat.getTruncateUnit().equals(ChronoUnit.FOREVER); } - private void processSetNullValues(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl, List> futures, long partition, DataType type) { - switch (type) { - case LONG: - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); - break; - case BOOLEAN: - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); - break; - case DOUBLE: - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); - break; - case STRING: - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); - break; - case JSON: - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); - futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); - break; - } - } - - private ListenableFuture saveNull(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl, long partition, DataType type) { - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind()); - stmtBuilder.setString(0, entityId.getEntityType().name()) - .setUuid(1, entityId.getId()) - .setString(2, tsKvEntry.getKey()) - .setLong(3, partition) - .setLong(4, tsKvEntry.getTs()); - stmtBuilder.setToNull(getColumnName(type)); - if (ttl > 0) { - stmtBuilder.setInt(6, (int) ttl); - } - BoundStatement stmt = stmtBuilder.build(); - return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); - } - private ListenableFuture doSavePartition(TenantId tenantId, EntityId entityId, String key, long ttl, long partition) { log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key); PreparedStatement preparedStatement = ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt(); @@ -591,6 +573,34 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD return deleteStmt; } + private PreparedStatement getSaveWithNullStmt() { + if (saveWithNullStmt == null) { + stmtCreationLock.lock(); + try { + if (saveWithNullStmt == null) { + saveWithNullStmt = prepare(INSERT_WITH_NULL); + } + } finally { + stmtCreationLock.unlock(); + } + } + return saveWithNullStmt; + } + + private PreparedStatement getSaveWithNullWithTtlStmt() { + if (saveWithNullWithTtlStmt == null) { + stmtCreationLock.lock(); + try { + if (saveWithNullWithTtlStmt == null) { + saveWithNullWithTtlStmt = prepare(INSERT_WITH_NULL + " USING TTL ?"); + } + } finally { + stmtCreationLock.unlock(); + } + } + return saveWithNullWithTtlStmt; + } + private PreparedStatement getSaveStmt(DataType dataType) { if (saveStmts == null) { stmtCreationLock.lock(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index ebe2d59dcf..8474af9584 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -38,7 +38,7 @@ import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.common.data.limit.LimitedApi; -import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.cache.limits.RateLimitService; import javax.annotation.Nullable; import java.util.HashMap; diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java index b0d42cdb07..f4a77dc725 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/DeviceConnectivityUtil.java @@ -112,12 +112,10 @@ public class DeviceConnectivityUtil { dockerComposeBuilder.append("# - \"5026:5026\" # Modbus TCP connector (Modbus Slave)\n"); dockerComposeBuilder.append("# - \"50000:50000/tcp\" # Socket connector with type TCP\n"); dockerComposeBuilder.append("# - \"50000:50000/udp\" # Socket connector with type UDP\n"); - if (isLocalhost(host)) { - dockerComposeBuilder.append("\n"); - dockerComposeBuilder.append(" # Necessary mapping for Linux\n"); - dockerComposeBuilder.append(" extra_hosts:\n"); - dockerComposeBuilder.append(" - \"host.docker.internal:host-gateway\"\n"); - } + dockerComposeBuilder.append("\n"); + dockerComposeBuilder.append(" # Necessary mapping for Linux\n"); + dockerComposeBuilder.append(" extra_hosts:\n"); + dockerComposeBuilder.append(" - \"host.docker.internal:host-gateway\"\n"); dockerComposeBuilder.append("\n"); dockerComposeBuilder.append(" # Environment variables\n"); dockerComposeBuilder.append(" environment:\n"); diff --git a/application/src/main/data/upgrade/3.0.1/schema_ts_latest.sql b/dao/src/main/resources/sql/schema-ts-latest-psql.sql similarity index 100% rename from application/src/main/data/upgrade/3.0.1/schema_ts_latest.sql rename to dao/src/main/resources/sql/schema-ts-latest-psql.sql diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java index 61d1c845fc..12bcac72f1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java @@ -16,12 +16,15 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.hibernate.exception.ConstraintViolationException; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @@ -35,8 +38,6 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; @@ -50,15 +51,19 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.service.validator.DeviceCredentialsDataValidator; import org.thingsboard.server.dao.tenant.TenantProfileService; import java.nio.ByteBuffer; +import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -79,6 +84,8 @@ public class DeviceServiceTest extends AbstractServiceTest { TenantProfileService tenantProfileService; @Autowired private PlatformTransactionManager platformTransactionManager; + @SpyBean + private DeviceCredentialsDataValidator validator; private IdComparator idComparator = new IdComparator<>(); private TenantId anotherTenantId; @@ -129,6 +136,67 @@ public class DeviceServiceTest extends AbstractServiceTest { }); } + @Test + public void testSaveDevicesWithTheSameAccessToken() { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(StringUtils.randomAlphabetic(10)); + device.setType("default"); + String accessToken = StringUtils.generateSafeToken(10); + Device savedDevice = deviceService.saveDeviceWithAccessToken(device, accessToken); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, savedDevice.getId()); + Assert.assertEquals(accessToken, deviceCredentials.getCredentialsId()); + + Device duplicatedDevice = new Device(); + duplicatedDevice.setTenantId(tenantId); + duplicatedDevice.setName(StringUtils.randomAlphabetic(10)); + duplicatedDevice.setType("default"); + assertThatThrownBy(() -> deviceService.saveDeviceWithAccessToken(duplicatedDevice, accessToken)) + .isInstanceOf(DeviceCredentialsValidationException.class) + .hasMessageContaining("Device credentials are already assigned to another device!"); + + Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, duplicatedDevice.getName()); + Assertions.assertNull(deviceByName); + } + + @Test + public void testShouldRollbackNotValidatedDeviceIfDeviceCredentialsValidationFailed() { + Mockito.reset(validator); + Mockito.doThrow(new DataValidationException("mock message")) + .when(validator).validate(any(), any()); + + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(StringUtils.randomAlphabetic(10)); + device.setType("default"); + assertThatThrownBy(() -> deviceService.saveDevice(device, false)) + .isInstanceOf(DataValidationException.class) + .hasMessageContaining("mock message"); + + Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, device.getName()); + Assertions.assertNull(deviceByName); + } + + @Test + public void testShouldRollbackValidatedDeviceIfDeviceCredentialsValidationFailed() { + Mockito.reset(validator); + Mockito.doThrow(new DataValidationException("mock message")) + .when(validator).validate(any(), any()); + + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(StringUtils.randomAlphabetic(10)); + device.setType("default"); + + assertThatThrownBy(() -> deviceService.saveDevice(device)) + .isInstanceOf(DataValidationException.class) + .hasMessageContaining("mock message"); + + Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, device.getName()); + Assertions.assertNull(deviceByName); + } + @Test public void testCountByTenantId() { Assert.assertEquals(0, deviceService.countByTenantId(tenantId)); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java index 2a03a1277c..2cc179fae2 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java @@ -40,7 +40,7 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.List; -import static org.apache.commons.lang3.time.DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT; +import static org.apache.commons.lang3.time.DateFormatUtils.ISO_8601_EXTENDED_DATETIME_FORMAT; @DaoSqlTest public class EdgeEventServiceTest extends AbstractServiceTest { @@ -56,19 +56,18 @@ public class EdgeEventServiceTest extends AbstractServiceTest { @Before public void before() throws ParseException { - timeBeforeStartTime = ISO_DATETIME_TIME_ZONE_FORMAT.parse("2016-11-01T11:30:00Z").getTime(); - startTime = ISO_DATETIME_TIME_ZONE_FORMAT.parse("2016-11-01T12:00:00Z").getTime(); - eventTime = ISO_DATETIME_TIME_ZONE_FORMAT.parse("2016-11-01T12:30:00Z").getTime(); - endTime = ISO_DATETIME_TIME_ZONE_FORMAT.parse("2016-11-01T13:00:00Z").getTime(); - timeAfterEndTime = ISO_DATETIME_TIME_ZONE_FORMAT.parse("2016-11-01T13:30:30Z").getTime(); + timeBeforeStartTime = ISO_8601_EXTENDED_DATETIME_FORMAT.parse("2016-11-01T11:30:00").getTime(); + startTime = ISO_8601_EXTENDED_DATETIME_FORMAT.parse("2016-11-01T12:00:00").getTime(); + eventTime = ISO_8601_EXTENDED_DATETIME_FORMAT.parse("2016-11-01T12:30:00").getTime(); + endTime = ISO_8601_EXTENDED_DATETIME_FORMAT.parse("2016-11-01T13:00:00").getTime(); + timeAfterEndTime = ISO_8601_EXTENDED_DATETIME_FORMAT.parse("2016-11-01T13:30:30").getTime(); } @Test public void saveEdgeEvent() throws Exception { EdgeId edgeId = new EdgeId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased()); - TenantId tenantId = new TenantId(Uuids.timeBased()); - EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.ADDED); + EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, deviceId); edgeEventService.saveAsync(edgeEvent).get(); PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, new TimePageLink(1)); @@ -81,9 +80,11 @@ public class EdgeEventServiceTest extends AbstractServiceTest { Assert.assertEquals(saved.getType(), edgeEvent.getType()); Assert.assertEquals(saved.getAction(), edgeEvent.getAction()); Assert.assertEquals(saved.getBody(), edgeEvent.getBody()); + + edgeEventService.cleanupEvents(1); } - protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType edgeEventAction) throws IOException { + protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId) throws IOException { if (tenantId == null) { tenantId = TenantId.fromUUID(Uuids.timeBased()); } @@ -92,7 +93,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest { edgeEvent.setEdgeId(edgeId); edgeEvent.setEntityId(entityId.getId()); edgeEvent.setType(EdgeEventType.DEVICE); - edgeEvent.setAction(edgeEventAction); + edgeEvent.setAction(EdgeEventActionType.ADDED); edgeEvent.setBody(readFromResource("TestJsonData.json")); return edgeEvent; } @@ -101,7 +102,6 @@ public class EdgeEventServiceTest extends AbstractServiceTest { public void findEdgeEventsByTimeDescOrder() throws Exception { EdgeId edgeId = new EdgeId(Uuids.timeBased()); DeviceId deviceId = new DeviceId(Uuids.timeBased()); - TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); List> futures = new ArrayList<>(); futures.add(saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId)); @@ -133,7 +133,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest { } private ListenableFuture saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception { - EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId, EdgeEventActionType.ADDED); + EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId); edgeEvent.setId(new EdgeEventId(Uuids.startOf(time))); return edgeEventService.saveAsync(edgeEvent); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java index ad351bb9f2..a624a2b6e5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; @@ -54,7 +55,9 @@ import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * @author Andrew Shvayka @@ -64,12 +67,12 @@ import static org.junit.Assert.assertNotNull; public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { @Autowired - TimeseriesService tsService; + protected TimeseriesService tsService; @Autowired EntityViewService entityViewService; - static final int MAX_TIMEOUT = 30; + protected static final int MAX_TIMEOUT = 30; private static final String STRING_KEY = "stringKey"; private static final String LONG_KEY = "longKey"; @@ -84,7 +87,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { KvEntry doubleKvEntry = new DoubleDataEntry(DOUBLE_KEY, Double.MAX_VALUE); KvEntry booleanKvEntry = new BooleanDataEntry(BOOLEAN_KEY, Boolean.TRUE); - private TenantId tenantId; + protected TenantId tenantId; @Before public void before() { @@ -673,6 +676,32 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { assertEquals(3, list.size()); } + @Test + public void shouldSaveEntryOfEachType() throws Exception { + BasicTsKvEntry booleanEntry = new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(1), new BooleanDataEntry("test", true)); + BasicTsKvEntry stringEntry = new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(2), new StringDataEntry("test", "text")); + BasicTsKvEntry longEntry = new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(3), new LongDataEntry("test", 15L)); + BasicTsKvEntry doubleEntry = new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(4), new DoubleDataEntry("test", 10.5)); + BasicTsKvEntry jsonEntry = new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(5), new JsonDataEntry("test", "{\"test\":\"testValue\"}")); + List timeseries = List.of(booleanEntry, stringEntry, longEntry, doubleEntry, jsonEntry); + + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + for (TsKvEntry tsKvEntry : timeseries) { + save(tenantId, deviceId, tsKvEntry); + } + + List listUntil3Minutes = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("test", 0L, + TimeUnit.MINUTES.toMillis(3), 1000, 10, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(2, listUntil3Minutes.size()); + assertThat(listUntil3Minutes).containsOnlyOnceElementsOf(List.of( + booleanEntry, stringEntry)); + + List fullList = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("test", 0L, + TimeUnit.MINUTES.toMillis(6), 1000, 10, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(5, fullList.size()); + assertThat(fullList).containsOnlyOnceElementsOf(timeseries); + } + private TsKvEntry save(DeviceId deviceId, long ts, long value) throws Exception { TsKvEntry entry = new BasicTsKvEntry(ts, new LongDataEntry(LONG_KEY, value)); tsService.save(tenantId, deviceId, entry).get(MAX_TIMEOUT, TimeUnit.SECONDS); @@ -691,6 +720,9 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { return entry; } + private void save(TenantId tenantId, DeviceId deviceId, TsKvEntry tsKvEntry) throws Exception { + tsService.save(tenantId, deviceId, tsKvEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + } private void saveEntries(DeviceId deviceId, long ts) throws ExecutionException, InterruptedException, TimeoutException { tsService.save(tenantId, deviceId, toTsEntry(ts, stringKvEntry)).get(MAX_TIMEOUT, TimeUnit.SECONDS); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlSetNullEnabledTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlSetNullEnabledTest.java new file mode 100644 index 0000000000..1bbb37659c --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlSetNullEnabledTest.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.dao.service.timeseries.nosql; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.dao.service.DaoNoSqlTest; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@DaoNoSqlTest +@TestPropertySource(properties = { + "cassandra.query.set_null_values_enabled=true", +}) +public class TimeseriesServiceNoSqlSetNullEnabledTest extends TimeseriesServiceNoSqlTest { + + @Override + @Test + public void testNullValuesOfNoneTargetColumn() throws ExecutionException, InterruptedException, TimeoutException { + long ts = TimeUnit.MINUTES.toMillis(1); + TsKvEntry longEntry = new BasicTsKvEntry(ts, new LongDataEntry("temp", 0L)); + double doubleValue = 20.6; + TsKvEntry doubleEntry = new BasicTsKvEntry(ts, new DoubleDataEntry("temp", doubleValue)); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + tsService.save(tenantId, deviceId, longEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + tsService.save(tenantId, deviceId, doubleEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + + List listWithoutAgg = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, + ts + 1 , 1000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(1, listWithoutAgg.size()); + assertFalse(listWithoutAgg.get(0).getLongValue().isPresent()); + assertTrue(listWithoutAgg.get(0).getDoubleValue().isPresent()); + assertThat(listWithoutAgg.get(0).getDoubleValue().get()).isEqualTo(doubleValue); + + // long value should be set to null after second insert, so avg = doubleValue + List listWithAgg = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, + ts + 1 , 1000, 3, Aggregation.AVG))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(1, listWithAgg.size()); + assertTrue(listWithAgg.get(0).getDoubleValue().isPresent()); + assertThat(listWithAgg.get(0).getDoubleValue().get()).isEqualTo(doubleValue); + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlTest.java index 229d5f5842..b8970feca3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlTest.java @@ -15,9 +15,83 @@ */ package org.thingsboard.server.dao.service.timeseries.nosql; +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.junit.Test; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.service.DaoNoSqlTest; import org.thingsboard.server.dao.service.timeseries.BaseTimeseriesServiceTest; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + @DaoNoSqlTest public class TimeseriesServiceNoSqlTest extends BaseTimeseriesServiceTest { + + @Test + public void shouldSaveEntryOfEachTypeWithTtl() throws ExecutionException, InterruptedException, TimeoutException { + long ttlInSec = TimeUnit.SECONDS.toSeconds(3); + List timeseries = List.of( + new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(1), new BooleanDataEntry("test", true)), + new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(2), new StringDataEntry("test", "text")), + new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(3), new LongDataEntry("test", 15L)), + new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(4), new DoubleDataEntry("test", 10.5)), + new BasicTsKvEntry(TimeUnit.MINUTES.toMillis(5), new JsonDataEntry("test", "{\"test\":\"testValue\"}"))); + + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + tsService.save(tenantId, deviceId, timeseries, ttlInSec); + + List fullList = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("test", 0L, + TimeUnit.MINUTES.toMillis(6), 1000, 10, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(5, fullList.size()); + + // check entries after ttl + Thread.sleep(TimeUnit.SECONDS.toMillis(ttlInSec + 1)); + List listAfterTtl = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("test", 0L, + TimeUnit.MINUTES.toMillis(6), 1000, 10, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(0, listAfterTtl.size()); + } + + @Test + public void testNullValuesOfNoneTargetColumn() throws ExecutionException, InterruptedException, TimeoutException { + long ts = TimeUnit.MINUTES.toMillis(1); + long longValue = 10L; + TsKvEntry longEntry = new BasicTsKvEntry(ts, new LongDataEntry("temp", longValue)); + double doubleValue = 20.6; + TsKvEntry doubleEntry = new BasicTsKvEntry(ts, new DoubleDataEntry("temp", doubleValue)); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + tsService.save(tenantId, deviceId, longEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + tsService.save(tenantId, deviceId, doubleEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + + List listWithoutAgg = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, + ts + 1 , 1000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(1, listWithoutAgg.size()); + assertTrue(listWithoutAgg.get(0).getLongValue().isPresent()); + assertFalse(listWithoutAgg.get(0).getDoubleValue().isPresent()); + assertThat(listWithoutAgg.get(0).getLongValue().get()).isEqualTo(longValue); + + // long value should not be reset to null, so avg = (doubleValue + longValue)/ 2 + List listWithAgg = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, + ts + 1, 200000, 3, Aggregation.AVG))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(1, listWithAgg.size()); + assertTrue(listWithAgg.get(0).getDoubleValue().isPresent()); + double expectedValue = (doubleValue + longValue)/ 2; + assertThat(listWithAgg.get(0).getDoubleValue().get()).isEqualTo(expectedValue); + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java index aa407e2a8e..f6ef6e8940 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java @@ -85,6 +85,6 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest { alarmComment.setUserId(new UserId(userId)); alarmComment.setType(type); alarmComment.setComment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))); - alarmCommentDao.createAlarmComment(TenantId.fromUUID(UUID.randomUUID()), alarmComment); + alarmCommentDao.save(TenantId.fromUUID(UUID.randomUUID()), alarmComment); } } diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 81a3e843c6..73c1495af8 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -127,20 +127,5 @@ queue.transport.poll_interval=5 queue.core.poll-interval=5 queue.core.partitions=2 queue.rule-engine.poll-interval=5 -queue.rule-engine.queues[0].poll-interval=5 -queue.rule-engine.queues[0].partitions=2 -queue.rule-engine.queues[0].processing-strategy.retries=1 -queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[1].poll-interval=5 -queue.rule-engine.queues[1].partitions=2 -queue.rule-engine.queues[1].processing-strategy.retries=1 -queue.rule-engine.queues[1].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[1].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[2].poll-interval=5 -queue.rule-engine.queues[2].partitions=2 -queue.rule-engine.queues[2].processing-strategy.retries=1 -queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0 spring.jpa.properties.hibernate.dialect=org.thingsboard.server.dao.ThingsboardPostgreSQLDialect \ No newline at end of file diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index d63a9f7f82..d72bb10b82 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/dao/src/test/resources/nosql-test.properties @@ -16,14 +16,3 @@ spring.datasource.password=postgres spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver spring.datasource.hikari.maximumPoolSize=16 - -queue.rule-engine.queues[0].name=Main -queue.rule-engine.queues[0].topic=tb_rule_engine.main -queue.rule-engine.queues[0].poll-interval=5 -queue.rule-engine.queues[0].partitions=2 -queue.rule-engine.queues[0].pack-processing-timeout=3000 -queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES -queue.rule-engine.queues[0].processing-strategy.retries=1 -queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[0].submit-strategy.type=BURST diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index 2fed33b43a..4292db28d6 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -41,15 +41,4 @@ service.type=monolith queue.core.pack-processing-timeout=3000 queue.rule-engine.pack-processing-timeout=3000 -queue.rule-engine.queues[0].name=Main -queue.rule-engine.queues[0].topic=tb_rule_engine.main -queue.rule-engine.queues[0].poll-interval=5 -queue.rule-engine.queues[0].partitions=2 -queue.rule-engine.queues[0].pack-processing-timeout=3000 -queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES -queue.rule-engine.queues[0].processing-strategy.retries=1 -queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0 -queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0 -queue.rule-engine.queues[0].submit-strategy.type=BURST - sql.log_entity_queries=true diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java index 8ee73110a0..eef0126f7f 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java @@ -21,6 +21,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; import org.thingsboard.common.util.ListeningExecutor; public interface MqttClient { @@ -32,7 +33,7 @@ public interface MqttClient { * @param host The ip address or host to connect to * @return A future which will be completed when the connection is opened and we received an CONNACK */ - Future connect(String host); + Promise connect(String host); /** * Connect to the specified hostname/ip using the specified port @@ -41,7 +42,7 @@ public interface MqttClient { * @param port The tcp port to connect to * @return A future which will be completed when the connection is opened and we received an CONNACK */ - Future connect(String host, int port); + Promise connect(String host, int port); /** * @@ -55,7 +56,7 @@ public interface MqttClient { * @return A future which will be completed when the connection is opened and we received an CONNACK * @throws IllegalStateException if no previous {@link #connect(String, int)} calls were attempted */ - Future reconnect(); + Promise reconnect(); /** * Retrieve the netty {@link EventLoopGroup} we are using diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index dedc670490..6781bb171a 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -118,7 +118,7 @@ final class MqttClientImpl implements MqttClient { * @return A future which will be completed when the connection is opened and we received an CONNACK */ @Override - public Future connect(String host) { + public Promise connect(String host) { return connect(host, 1883); } @@ -130,11 +130,11 @@ final class MqttClientImpl implements MqttClient { * @return A future which will be completed when the connection is opened and we received an CONNACK */ @Override - public Future connect(String host, int port) { + public Promise connect(String host, int port) { return connect(host, port, false); } - private Future connect(String host, int port, boolean reconnect) { + private Promise connect(String host, int port, boolean reconnect) { log.trace("[{}] Connecting to server, isReconnect - {}", channel != null ? channel.id() : "UNKNOWN", reconnect); if (this.eventLoop == null) { this.eventLoop = new NioEventLoopGroup(); @@ -199,7 +199,7 @@ final class MqttClientImpl implements MqttClient { } @Override - public Future reconnect() { + public Promise reconnect() { log.trace("[{}] Reconnecting to server, isReconnect - {}", channel != null ? channel.id() : "UNKNOWN", reconnect); if (host == null) { throw new IllegalStateException("Cannot reconnect. Call connect() first"); diff --git a/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java b/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java index 8a398eeec5..04103bf67b 100644 --- a/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java +++ b/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java @@ -21,6 +21,7 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; @@ -127,7 +128,7 @@ public class MqttIntegrationTest { config.setReconnectDelay(RECONNECT_DELAY_SECONDS); MqttClient client = MqttClient.create(config, null, handlerExecutor); client.setEventLoop(this.eventLoopGroup); - Future connectFuture = client.connect(MQTT_HOST, this.mqttServer.getMqttPort()); + Promise connectFuture = client.connect(MQTT_HOST, this.mqttServer.getMqttPort()); String hostPort = MQTT_HOST + ":" + this.mqttServer.getMqttPort(); MqttConnectResult result; diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index f3a7ee77c7..8783770be3 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -525,12 +525,11 @@ public class RestClient implements Closeable { } public PageData getAlarmComments(AlarmId alarmId, PageLink pageLink) { - String urlSecondPart = "/api/alarm/{alarmId}/comment"; Map params = new HashMap<>(); params.put("alarmId", alarmId.getId().toString()); - + addPageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + urlSecondPart + "&" + getUrlParams(pageLink), + baseURL + "/api/alarm/{alarmId}/comment?" + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java index 31fda250d0..4176ead31c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java @@ -47,6 +47,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.TO_SERVER_RPC_REQUEST; @Slf4j public abstract class AbstractTbMsgPushNode implements TbNode { @@ -176,6 +177,6 @@ public abstract class AbstractTbMsgPushNode
" + + "If an object with coordinates extracted from incoming message enters the geofence, sends a message with the type Entered. " + + "If an object leaves the geofence, sends a message with the type Left. " + + "If the presence monitoring strategy \"On first message\" is selected, sends messages via rule node connection type Inside or Outside only the first time the geofencing and duration conditions are satisfied; otherwise sends messages via rule node connection type Success. " + + "If the presence monitoring strategy \"On each message\" is selected, sends messages via rule node connection type Inside or Outside every time the geofencing condition is satisfied. " + + "

" + + "Output connections: Entered, Left, Inside, Outside, Success", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGpsGeofencingConfig" ) public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { + private static final String REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE = "reportPresenceStatusOnEachMessage"; private final Map entityStates = new HashMap<>(); private final Gson gson = new Gson(); private final JsonParser parser = new JsonParser(); @@ -80,25 +97,32 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode (entityState.isInside() ? - TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) : TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) { - setStaid(ctx, msg.getOriginator(), entityState); - ctx.tellNext(msg, entityState.isInside() ? "Inside" : "Outside"); - told = true; - } - } + ctx.tellNext(msg, matches ? ENTERED : LEFT); + return; + } + + if (config.isReportPresenceStatusOnEachMessage()) { + ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE); + return; } - if (!told) { + + if (entityState.isStayed()) { ctx.tellSuccess(msg); + return; + } + + long stayTime = ts - entityState.getStateSwitchTime(); + if (stayTime > (entityState.isInside() ? + TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) : + TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) { + setStaid(ctx, msg.getOriginator(), entityState); + ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE); + return; } + + ctx.tellSuccess(msg); } private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) { @@ -127,4 +151,17 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode getConfigClazz() { return TbGpsGeofencingActionNodeConfiguration.class; } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + boolean hasChanges = false; + if (fromVersion == 0) { + if (!oldConfiguration.has(REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE)) { + hasChanges = true; + ((ObjectNode) oldConfiguration).put(REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE, false); + } + } + return new TbPair<>(hasChanges, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java index d0adad8996..04bbab01b7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeConfiguration.java @@ -31,6 +31,8 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte private String minInsideDurationTimeUnit; private String minOutsideDurationTimeUnit; + private boolean reportPresenceStatusOnEachMessage; + @Override public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() { TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration(); @@ -43,6 +45,7 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name()); configuration.setMinInsideDuration(1); configuration.setMinOutsideDuration(1); + configuration.setReportPresenceStatusOnEachMessage(true); return configuration; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 7632402e5e..6e80a1577b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -18,7 +18,7 @@ package org.thingsboard.rule.engine.mqtt; import io.netty.buffer.Unpooled; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.ssl.SslContext; -import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; import lombok.extern.slf4j.Slf4j; import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClientConfig; @@ -121,7 +121,7 @@ public class TbMqttNode extends TbAbstractExternalNode { prepareMqttClientConfig(config); MqttClient client = MqttClient.create(config, null, ctx.getExternalCallExecutor()); client.setEventLoop(ctx.getSharedEventLoop()); - Future connectFuture = client.connect(this.mqttNodeConfiguration.getHost(), this.mqttNodeConfiguration.getPort()); + Promise connectFuture = client.connect(this.mqttNodeConfiguration.getHost(), this.mqttNodeConfiguration.getPort()); MqttConnectResult result; try { result = connectFuture.get(this.mqttNodeConfiguration.getConnectTimeoutSec(), TimeUnit.SECONDS); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java index 80d73b9478..4d651f48fa 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -63,6 +64,9 @@ public class EntitiesFieldsAsyncLoader { case ENTITY_VIEW: return toEntityFieldsDataAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) originatorId), EntityFieldsData::new, ctx); + case EDGE: + return toEntityFieldsDataAsync(ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), (EdgeId) originatorId), + EntityFieldsData::new, ctx); default: return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType: " + originatorId.getEntityType())); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/GpsGeofencingEvents.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/GpsGeofencingEvents.java new file mode 100644 index 0000000000..db5ddaf698 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/GpsGeofencingEvents.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.rule.engine.util; + +public class GpsGeofencingEvents { + public static final String ENTERED = "Entered"; + public static final String INSIDE = "Inside"; + public static final String LEFT = "Left"; + public static final String OUTSIDE = "Outside"; +} diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index 9b31fc9a13..5bfa26ada4 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1,25 +1,25 @@ -System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/common","@angular/material/checkbox","@angular/material/input","@angular/material/form-field","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/select","@angular/material/core","@angular/material/slide-toggle","@shared/components/hint-tooltip-icon.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/material/icon","@angular/material/tooltip","@shared/components/script-lang.component","@angular/cdk/keycodes","@angular/material/chips","@shared/pipe/safe.pipe","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/flex-layout/extended","@angular/material/list","@angular/cdk/drag-drop","rxjs/operators","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@home/components/public-api","tslib","rxjs","@shared/components/help-popup.component","@shared/components/entity/entity-subtype-list.component","@shared/components/relation/relation-type-autocomplete.component","@home/components/relation/relation-filters.component","@angular/material/expansion","@shared/components/file-input.component","@shared/components/button/toggle-password.component","@shared/components/string-items-list.component","@shared/components/toggle-header.component","@shared/components/toggle-select.component","@shared/components/entity/entity-list.component","@shared/components/notification/template-autocomplete.component","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@angular/material/radio","@shared/components/slack-conversation-autocomplete.component","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component","@angular/cdk/platform"],(function(e){"use strict";var t,n,r,o,a,i,l,s,m,p,d,u,c,f,g,y,x,b,h,v,C,F,k,L,T,I,N,S,q,A,M,E,G,D,w,V,P,R,O,_,B,K,z,U,H,j,$,Q,J,Y,W,Z,X,ee,te,ne,re,oe,ae,ie,le,se,me,pe,de,ue,ce,fe,ge,ye,xe,be,he,ve,Ce,Fe,ke,Le,Te,Ie,Ne,Se,qe,Ae,Me,Ee,Ge,De,we,Ve,Pe,Re,Oe,_e,Be,Ke,ze,Ue,He,je,$e,Qe,Je,Ye,We,Ze,Xe,et,tt,nt,rt,ot,at,it,lt,st,mt,pt;return{setters:[function(e){t=e,n=e.Component,r=e.EventEmitter,o=e.ViewChild,a=e.forwardRef,i=e.Input,l=e.InjectionToken,s=e.Injectable,m=e.Inject,p=e.Optional,d=e.Directive,u=e.Output,c=e.NgModule},function(e){f=e.RuleNodeConfigurationComponent,g=e.AttributeScope,y=e.telemetryTypeTranslations,x=e.ScriptLanguage,b=e.AlarmSeverity,h=e.alarmSeverityTranslations,v=e.EntitySearchDirection,C=e.entitySearchDirectionTranslations,F=e.EntityType,k=e.entityFields,L=e.PageComponent,T=e.coerceBoolean,I=e.MessageType,N=e.messageTypeNames,S=e,q=e.AlarmStatus,A=e.alarmStatusTranslations,M=e.SharedModule,E=e.AggregationType,G=e.aggregationTranslations,D=e.NotificationType,w=e.SlackChanelType,V=e.SlackChanelTypesTranslateMap},function(e){P=e},function(e){R=e,O=e.Validators,_=e.NgControl,B=e.NG_VALUE_ACCESSOR,K=e.NG_VALIDATORS,z=e.FormArray,U=e.FormGroup},function(e){H=e,j=e.DOCUMENT,$=e.CommonModule},function(e){Q=e},function(e){J=e},function(e){Y=e},function(e){W=e},function(e){Z=e},function(e){X=e},function(e){ee=e},function(e){te=e},function(e){ne=e},function(e){re=e.getCurrentAuthState,oe=e,ae=e.isEqual,ie=e.isDefinedAndNotNull,le=e.deepTrim,se=e.isObject,me=e.isNotEmptyStr},function(e){pe=e},function(e){de=e},function(e){ue=e},function(e){ce=e},function(e){fe=e},function(e){ge=e.ENTER,ye=e.COMMA,xe=e.SEMICOLON},function(e){be=e},function(e){he=e},function(e){ve=e},function(e){Ce=e},function(e){Fe=e.coerceBooleanProperty,ke=e.coerceElement,Le=e.coerceNumberProperty},function(e){Te=e},function(e){Ie=e},function(e){Ne=e},function(e){Se=e},function(e){qe=e.tap,Ae=e.map,Me=e.startWith,Ee=e.mergeMap,Ge=e.share,De=e.takeUntil,we=e.auditTime},function(e){Ve=e},function(e){Pe=e},function(e){Re=e.HomeComponentsModule},function(e){Oe=e.__decorate},function(e){_e=e.Subject,Be=e.takeUntil,Ke=e.of,ze=e.EMPTY,Ue=e.fromEvent},function(e){He=e},function(e){je=e},function(e){$e=e},function(e){Qe=e},function(e){Je=e},function(e){Ye=e},function(e){We=e},function(e){Ze=e},function(e){Xe=e},function(e){et=e},function(e){tt=e},function(e){nt=e},function(e){rt=e},function(e){ot=e},function(e){at=e},function(e){it=e},function(e){lt=e},function(e){st=e},function(e){mt=e.normalizePassiveListenerOptions,pt=e}],execute:function(){class dt extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",dt),dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dt,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dt,decorators:[{type:n,args:[{selector:"tb-node-empty-config",template:"
"}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ut extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[O.required,O.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[O.required,O.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",ut),ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ut,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ut,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ut,decorators:[{type:n,args:[{selector:"tb-action-node-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ct extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]],updateAttributesOnlyOnValueChange:[!!e&&e.updateAttributesOnlyOnValueChange,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===g.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1}),this.attributesConfigForm.get("updateAttributesOnlyOnValueChange").patchValue(!1,{emitEvent:!1})}))}}e("AttributesConfigComponent",ct),ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ct,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,decorators:[{type:n,args:[{selector:"tb-action-node-attributes-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ft extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[O.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===x.JS?[O.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===x.TBEL?[O.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.clearAlarmConfigForm.get("scriptLang").value,n=t===x.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===x.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",o=this.clearAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.clearAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",ft),ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ft,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,decorators:[{type:n,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class gt extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.alarmSeverities=Object.keys(b),this.alarmSeverityTranslationMap=h,this.separatorKeysCodes=[ge,ye,xe],this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([O.required]),this.createAlarmConfigForm.get("severity").setValidators([O.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==x.TBEL||this.tbelEnabled||(r=x.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const o=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(o&&r===x.JS?[O.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(o&&r===x.TBEL?[O.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===x.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===x.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",o=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",gt),gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gt,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,decorators:[{type:n,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class yt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.entityType=F}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[O.required]],entityType:[e?e.entityType:null,[O.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[O.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[O.required,O.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==F.DEVICE&&t!==F.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([O.required,O.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",yt),yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yt,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,decorators:[{type:n,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class xt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.entityType=F}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[O.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[O.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[O.required,O.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([O.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",xt),xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xt,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,decorators:[{type:n,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class bt extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,O.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,O.required]})}}e("DeviceProfileConfigComponent",bt),bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bt,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,decorators:[{type:n,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ht extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function"}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[O.required,O.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[O.required,O.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",o=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}var vt;e("GeneratorConfigComponent",ht),ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ht,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Ce.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,decorators:[{type:n,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(vt||(vt={}));const Ct=new Map([[vt.CUSTOMER,"tb.rulenode.originator-customer"],[vt.TENANT,"tb.rulenode.originator-tenant"],[vt.RELATED,"tb.rulenode.originator-related"],[vt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[vt.ENTITY,"tb.rulenode.originator-entity"]]),Ft=new Map([[vt.CUSTOMER,"tb.rulenode.originator-customer-desc"],[vt.TENANT,"tb.rulenode.originator-tenant-desc"],[vt.RELATED,"tb.rulenode.originator-related-entity-desc"],[vt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator-desc"],[vt.ENTITY,"tb.rulenode.originator-entity-by-name-pattern-desc"]]),kt=[k.createdTime,k.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},k.firstName,k.lastName,k.email,k.title,k.country,k.state,k.city,k.address,k.address2,k.zip,k.phone,k.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],Lt=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var Tt;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(Tt||(Tt={}));const It=new Map([[Tt.CIRCLE,"tb.rulenode.perimeter-circle"],[Tt.POLYGON,"tb.rulenode.perimeter-polygon"]]);var Nt;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(Nt||(Nt={}));const St=new Map([[Nt.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[Nt.SECONDS,"tb.rulenode.time-unit-seconds"],[Nt.MINUTES,"tb.rulenode.time-unit-minutes"],[Nt.HOURS,"tb.rulenode.time-unit-hours"],[Nt.DAYS,"tb.rulenode.time-unit-days"]]);var qt;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(qt||(qt={}));const At=new Map([[qt.METER,"tb.rulenode.range-unit-meter"],[qt.KILOMETER,"tb.rulenode.range-unit-kilometer"],[qt.FOOT,"tb.rulenode.range-unit-foot"],[qt.MILE,"tb.rulenode.range-unit-mile"],[qt.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var Mt;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(Mt||(Mt={}));const Et=new Map([[Mt.ID,"tb.rulenode.entity-details-id"],[Mt.TITLE,"tb.rulenode.entity-details-title"],[Mt.COUNTRY,"tb.rulenode.entity-details-country"],[Mt.STATE,"tb.rulenode.entity-details-state"],[Mt.CITY,"tb.rulenode.entity-details-city"],[Mt.ZIP,"tb.rulenode.entity-details-zip"],[Mt.ADDRESS,"tb.rulenode.entity-details-address"],[Mt.ADDRESS2,"tb.rulenode.entity-details-address2"],[Mt.PHONE,"tb.rulenode.entity-details-phone"],[Mt.EMAIL,"tb.rulenode.entity-details-email"],[Mt.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Gt;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Gt||(Gt={}));const Dt=new Map([[Gt.FIRST,"tb.rulenode.first"],[Gt.LAST,"tb.rulenode.last"],[Gt.ALL,"tb.rulenode.all"]]),wt=new Map([[Gt.FIRST,"tb.rulenode.first-mode-hint"],[Gt.LAST,"tb.rulenode.last-mode-hint"],[Gt.ALL,"tb.rulenode.all-mode-hint"]]);var Vt,Pt;!function(e){e.ASC="ASC",e.DESC="DESC"}(Vt||(Vt={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(Pt||(Pt={}));const Rt=new Map([[Pt.ATTRIBUTES,"tb.rulenode.attributes"],[Pt.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[Pt.FIELDS,"tb.rulenode.fields"]]),Ot=new Map([[Pt.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[Pt.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[Pt.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),_t=new Map([[Vt.ASC,"tb.rulenode.ascending"],[Vt.DESC,"tb.rulenode.descending"]]);var Bt;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Bt||(Bt={}));const Kt=new Map([[Bt.STANDARD,"tb.rulenode.sqs-queue-standard"],[Bt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),zt=["anonymous","basic","cert.PEM"],Ut=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Ht=["sas","cert.PEM"],jt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var $t;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}($t||($t={}));const Qt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],Jt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Yt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Yt||(Yt={}));const Wt=new Map([[Yt.CUSTOM,{value:Yt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Yt.ADD,{value:Yt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Yt.SUB,{value:Yt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Yt.MULT,{value:Yt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Yt.DIV,{value:Yt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Yt.SIN,{value:Yt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Yt.SINH,{value:Yt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Yt.COS,{value:Yt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Yt.COSH,{value:Yt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Yt.TAN,{value:Yt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Yt.TANH,{value:Yt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Yt.ACOS,{value:Yt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Yt.ASIN,{value:Yt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Yt.ATAN,{value:Yt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Yt.ATAN2,{value:Yt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Yt.EXP,{value:Yt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Yt.EXPM1,{value:Yt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Yt.SQRT,{value:Yt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Yt.CBRT,{value:Yt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Yt.GET_EXP,{value:Yt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Yt.HYPOT,{value:Yt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Yt.LOG,{value:Yt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Yt.LOG10,{value:Yt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Yt.LOG1P,{value:Yt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Yt.CEIL,{value:Yt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Yt.FLOOR,{value:Yt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Yt.FLOOR_DIV,{value:Yt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Yt.FLOOR_MOD,{value:Yt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Yt.ABS,{value:Yt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Yt.MIN,{value:Yt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Yt.MAX,{value:Yt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Yt.POW,{value:Yt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Yt.SIGNUM,{value:Yt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Yt.RAD,{value:Yt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Yt.DEG,{value:Yt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var Zt,Xt,en;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(Zt||(Zt={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(Xt||(Xt={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(en||(en={}));const tn=new Map([[en.DATA,"tb.rulenode.message-to-metadata"],[en.METADATA,"tb.rulenode.metadata-to-message"]]),nn=(new Map([[en.DATA,"tb.rulenode.from-message"],[en.METADATA,"tb.rulenode.from-metadata"]]),new Map([[en.DATA,"tb.rulenode.message"],[en.METADATA,"tb.rulenode.metadata"]])),rn=new Map([[en.DATA,"tb.rulenode.message"],[en.METADATA,"tb.rulenode.message-metadata"]]),on=new Map([[Zt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[Zt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[Zt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[Zt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[Zt.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),an=new Map([[Xt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[Xt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[Xt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[Xt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),ln=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var sn,mn;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(sn||(sn={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}(mn||(mn={}));const pn=new Map([[sn.SHARED_SCOPE,"tb.rulenode.shared-scope"],[sn.SERVER_SCOPE,"tb.rulenode.server-scope"],[sn.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class dn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Tt,this.perimeterTypes=Object.keys(Tt),this.perimeterTypeTranslationMap=It,this.rangeUnits=Object.keys(qt),this.rangeUnitTranslationMap=At,this.timeUnits=Object.keys(Nt),this.timeUnitsTranslationMap=St}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[O.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[O.required]],perimeterType:[e?e.perimeterType:null,[O.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[O.required,O.min(1),O.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[O.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[O.required,O.min(1),O.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[O.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([O.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==Tt.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([O.required,O.min(-90),O.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([O.required,O.min(-180),O.max(180)]),this.geoActionConfigForm.get("range").setValidators([O.required,O.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([O.required])),t||n!==Tt.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([O.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",dn),dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dn,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,decorators:[{type:n,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class un extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",o=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",un),un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:un,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,decorators:[{type:n,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class cn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[O.required,O.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[O.required]]})}}e("MsgCountConfigComponent",cn),cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cn,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class fn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[O.required,O.min(1),O.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([O.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([O.required,O.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",fn),fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fn,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class gn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]]})}}e("PushToCloudConfigComponent",gn),gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gn,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class yn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]]})}}e("PushToEdgeConfigComponent",yn),yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yn,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class xn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",xn),xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xn,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class bn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[O.required,O.min(0)]]})}}e("RpcRequestConfigComponent",bn),bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bn,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class hn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[O.required]],value:[e[n],[O.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[O.required]],value:["",[O.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigOldComponent",hn),hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hn,selector:"tb-kv-map-config-old",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:B,useExisting:a((()=>hn)),multi:!0},{provide:K,useExisting:a((()=>hn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Te.TbErrorComponent,selector:"tb-error",inputs:["noMargin","error"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Ie.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,decorators:[{type:n,args:[{selector:"tb-kv-map-config-old",providers:[{provide:B,useExisting:a((()=>hn)),multi:!0},{provide:K,useExisting:a((()=>hn)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class vn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[O.required,O.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[O.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",vn),vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vn,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,decorators:[{type:n,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Cn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[O.required,O.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",Cn),Cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cn,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,decorators:[{type:n,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Fn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[O.required,O.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[O.required,O.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Fn),Fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fn,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,decorators:[{type:n,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class kn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y,this.separatorKeysCodes=[ge,ye,xe]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]],keys:[e?e.keys:null,[O.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",kn),kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kn,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,decorators:[{type:n,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:o,args:["attributeChipList"]}]}});class Ln extends L{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t){super(e),this.store=e,this.fb=t,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=Wt,this.ArgumentType=Zt,this.attributeScopeMap=pn,this.argumentTypeMap=on,this.arguments=Object.values(Zt),this.attributeScope=Object.values(sn),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray,n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}get argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormArray.controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormArray.removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormArray,n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Yt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([O.minLength(this.minArgs),O.maxLength(this.maxArgs)]);this.argumentsFormArray.length>this.maxArgs;)this.removeArgument(this.maxArgs-1);for(;this.argumentsFormArray.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===Zt.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==Zt.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormArray.controls.forEach(((e,t)=>{e.get("name").setValue(ln[t])}))}updateModel(){const e=this.argumentsFormArray.value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",Ln),Ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ln,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:B,useExisting:a((()=>Ln)),multi:!0},{provide:K,useExisting:a((()=>Ln)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"],dependencies:[{kind:"directive",type:H.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Ne.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:Ne.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:Se.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:Se.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:Se.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Ie.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,decorators:[{type:n,args:[{selector:"tb-arguments-map-config",providers:[{provide:B,useExisting:a((()=>Ln)),multi:!0},{provide:K,useExisting:a((()=>Ln)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class Tn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[...Wt.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(qe((e=>{let t;t="string"==typeof e&&Yt[e]?Yt[e]:null,this.updateView(t)})),Ae((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=Wt.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",Tn),Tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tn,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:B,useExisting:a((()=>Tn)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Pe.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,decorators:[{type:n,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:B,useExisting:a((()=>Tn)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:o,args:["operationInput",{static:!0}]}]}});class In extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Yt,this.ArgumentTypeResult=Xt,this.argumentTypeResultMap=an,this.attributeScopeMap=pn,this.argumentsResult=Object.values(Xt),this.attributeScopeResult=Object.values(mn)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[O.required]],arguments:[e?e.arguments:null,[O.required]],customFunction:[e?e.customFunction:"",[O.required]],result:this.fb.group({type:[e?e.result.type:null,[O.required]],attributeScope:[e?e.result.attributeScope:null,[O.required]],key:[e?e.result.key:"",[O.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Yt.CUSTOM?(this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}),null===this.mathFunctionConfigForm.get("customFunction").value&&this.mathFunctionConfigForm.get("customFunction").patchValue("(x - 32) / 1.8",{emitEvent:!1})):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===Xt.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",In),In.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),In.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:In,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ln,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:Tn,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,decorators:[{type:n,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Nn{constructor(){this.textAlign="left"}}e("ExampleHintComponent",Nn),Nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,deps:[],target:t.ɵɵFactoryTarget.Component}),Nn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Nn,selector:"tb-example-hint",inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink",textAlign:"textAlign"},ngImport:t,template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,decorators:[{type:n,args:[{selector:"tb-example-hint",template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"]}]}],propDecorators:{hintText:[{type:i}],popupHelpLink:[{type:i}],textAlign:[{type:i}]}});class Sn{constructor(e,t){this.injector=e,this.fb=t,this.propagateChange=()=>{},this.destroy$=new _e,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value&&e.controls.key.value&&e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof z||e instanceof U){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return ae(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(Be(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(){const e=this.kvListFormGroup.get("keyVals").value;if(!e.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const t of e)if(t.key===t.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",Sn),Sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,deps:[{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Sn,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:B,useExisting:a((()=>Sn)),multi:!0},{provide:K,useExisting:a((()=>Sn)),multi:!0}],ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],Sn.prototype,"disabled",void 0),Oe([T()],Sn.prototype,"uniqueKeyValuePairValidator",void 0),Oe([T()],Sn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,decorators:[{type:n,args:[{selector:"tb-kv-map-config",providers:[{provide:B,useExisting:a((()=>Sn)),multi:!0},{provide:K,useExisting:a((()=>Sn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class qn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(v),this.directionTypeTranslations=C,this.entityType=F,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[O.min(1)]],relationType:[null],deviceTypes:[null,[O.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",qn),qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:qn,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>qn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:je.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","floatLabel","label","disabled","entityType","emptyInputPlaceholder","filledInputPlaceholder","appearance","subscriptSizing","additionalClasses"]},{kind:"component",type:$e.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,decorators:[{type:n,args:[{selector:"tb-device-relations-query-config",providers:[{provide:B,useExisting:a((()=>qn)),multi:!0}],template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class An extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(v),this.directionTypeTranslations=C,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[O.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",An),An.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),An.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:An,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>An)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Qe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,decorators:[{type:n,args:[{selector:"tb-relations-query-config",providers:[{provide:B,useExisting:a((()=>An)),multi:!0}],template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Mn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[ge,ye,xe],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(I))this.messageTypesList.push({name:N.get(I[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Me(""),Ae((e=>e||"")),Ee((e=>this.fetchMessageTypes(e))),Ge())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ke(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ke(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",Mn),Mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,deps:[{token:P.Store},{token:Z.TranslateService},{token:S.TruncatePipe},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Mn,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:B,useExisting:a((()=>Mn)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:Ve.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Pe.HighlightPipe,name:"highlight"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,decorators:[{type:n,args:[{selector:"tb-message-types-config",providers:[{provide:B,useExisting:a((()=>Mn)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:S.TruncatePipe},{type:R.FormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:o,args:["chipList",{static:!1}]}],matAutocomplete:[{type:o,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:o,args:["messageTypeInput",{static:!1}]}]}});class En extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=zt,this.credentialsTypeTranslationsMap=Ut,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[O.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){ie(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([O.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[O.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(O.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",En),En.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),En.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:En,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:B,useExisting:a((()=>En)),multi:!0},{provide:K,useExisting:a((()=>En)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:H.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Je.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Je.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,decorators:[{type:n,args:[{selector:"tb-credentials-config",providers:[{provide:B,useExisting:a((()=>En)),multi:!0},{provide:K,useExisting:a((()=>En)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});const Gn=new l("WindowToken","undefined"!=typeof window&&window.document?{providedIn:"root",factory:()=>window}:{providedIn:"root",factory:()=>{}});class Dn{constructor(e,t,n){this.ngZone=e,this.document=t,this.window=n,this.copySubject=new _e,this.copyResponse$=this.copySubject.asObservable(),this.config={}}configure(e){this.config=e}copy(e){if(!this.isSupported||!e)return this.pushCopyResponse({isSuccess:!1,content:e});const t=this.copyFromContent(e);return t?this.pushCopyResponse({content:e,isSuccess:t}):this.pushCopyResponse({isSuccess:!1,content:e})}get isSupported(){return!!this.document.queryCommandSupported&&!!this.document.queryCommandSupported("copy")&&!!this.window}isTargetValid(e){if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement){if(e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');return!0}throw new Error("Target should be input or textarea")}copyFromInputElement(e,t=!0){try{this.selectTarget(e);const n=this.copyText();return this.clearSelection(t?e:void 0,this.window),n&&this.isCopySuccessInIE11()}catch(e){return!1}}isCopySuccessInIE11(){const e=this.window.clipboardData;return!(e&&e.getData&&!e.getData("Text"))}copyFromContent(e,t=this.document.body){if(this.tempTextArea&&!t.contains(this.tempTextArea)&&this.destroy(this.tempTextArea.parentElement||void 0),!this.tempTextArea){this.tempTextArea=this.createTempTextArea(this.document,this.window);try{t.appendChild(this.tempTextArea)}catch(e){throw new Error("Container should be a Dom element")}}this.tempTextArea.value=e;const n=this.copyFromInputElement(this.tempTextArea,!1);return this.config.cleanUpAfterCopy&&this.destroy(this.tempTextArea.parentElement||void 0),n}destroy(e=this.document.body){this.tempTextArea&&(e.removeChild(this.tempTextArea),this.tempTextArea=void 0)}selectTarget(e){return e.select(),e.setSelectionRange(0,e.value.length),e.value.length}copyText(){return this.document.execCommand("copy")}clearSelection(e,t){e&&e.focus(),t.getSelection()?.removeAllRanges()}createTempTextArea(e,t){const n="rtl"===e.documentElement.getAttribute("dir");let r;r=e.createElement("textarea"),r.style.fontSize="12pt",r.style.border="0",r.style.padding="0",r.style.margin="0",r.style.position="absolute",r.style[n?"right":"left"]="-9999px";const o=t.pageYOffset||e.documentElement.scrollTop;return r.style.top=o+"px",r.setAttribute("readonly",""),r}pushCopyResponse(e){this.copySubject.observers.length>0&&this.ngZone.run((()=>{this.copySubject.next(e)}))}pushCopyReponse(e){this.pushCopyResponse(e)}}Dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,deps:[{token:t.NgZone},{token:j},{token:Gn,optional:!0}],target:t.ɵɵFactoryTarget.Injectable}),Dn.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:void 0,decorators:[{type:m,args:[j]}]},{type:void 0,decorators:[{type:p},{type:m,args:[Gn]}]}]}});class wn{constructor(e,t,n,o){this.ngZone=e,this.host=t,this.renderer=n,this.clipboardSrv=o,this.cbOnSuccess=new r,this.cbOnError=new r,this.onClick=e=>{this.clipboardSrv.isSupported?this.targetElm&&this.clipboardSrv.isTargetValid(this.targetElm)?this.handleResult(this.clipboardSrv.copyFromInputElement(this.targetElm),this.targetElm.value,e):this.cbContent&&this.handleResult(this.clipboardSrv.copyFromContent(this.cbContent,this.container),this.cbContent,e):this.handleResult(!1,void 0,e)}}ngOnInit(){this.ngZone.runOutsideAngular((()=>{this.clickListener=this.renderer.listen(this.host.nativeElement,"click",this.onClick)}))}ngOnDestroy(){this.clickListener&&this.clickListener(),this.clipboardSrv.destroy(this.container)}handleResult(e,t,n){let r={isSuccess:e,content:t,successMessage:this.cbSuccessMsg,event:n};e?this.cbOnSuccess.observed&&this.ngZone.run((()=>{this.cbOnSuccess.emit(r)})):this.cbOnError.observed&&this.ngZone.run((()=>{this.cbOnError.emit(r)})),this.clipboardSrv.pushCopyResponse(r)}}wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:wn,deps:[{token:t.NgZone},{token:t.ElementRef},{token:t.Renderer2},{token:Dn}],target:t.ɵɵFactoryTarget.Directive}),wn.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:wn,selector:"[ngxClipboard]",inputs:{targetElm:["ngxClipboard","targetElm"],container:"container",cbContent:"cbContent",cbSuccessMsg:"cbSuccessMsg"},outputs:{cbOnSuccess:"cbOnSuccess",cbOnError:"cbOnError"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:wn,decorators:[{type:d,args:[{selector:"[ngxClipboard]"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:t.ElementRef},{type:t.Renderer2},{type:Dn}]},propDecorators:{targetElm:[{type:i,args:["ngxClipboard"]}],container:[{type:i}],cbContent:[{type:i}],cbSuccessMsg:[{type:i}],cbOnSuccess:[{type:u}],cbOnError:[{type:u}]}});class Vn{constructor(e,t,n){this._clipboardService=e,this._viewContainerRef=t,this._templateRef=n}ngOnInit(){this._clipboardService.isSupported&&this._viewContainerRef.createEmbeddedView(this._templateRef)}}Vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Vn,deps:[{token:Dn},{token:t.ViewContainerRef},{token:t.TemplateRef}],target:t.ɵɵFactoryTarget.Directive}),Vn.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:Vn,selector:"[ngxClipboardIfSupported]",ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Vn,decorators:[{type:d,args:[{selector:"[ngxClipboardIfSupported]"}]}],ctorParameters:function(){return[{type:Dn},{type:t.ViewContainerRef},{type:t.TemplateRef}]}});class Pn{}Pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Pn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,declarations:[wn,Vn],imports:[$],exports:[wn,Vn]}),Pn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,imports:[[$]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,decorators:[{type:c,args:[{imports:[$],declarations:[wn,Vn],exports:[wn,Vn]}]}]});class Rn{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e){this.fb=e,this.subscriptSizing="fixed",this.messageTypes=[{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},{name:"Custom",value:""}],this.propagateChange=()=>{},this.destroy$=new _e,this.messageTypeFormGroup=this.fb.group({messageTypeAlias:[null,[O.required]],messageType:[{value:null,disabled:!0},[O.maxLength(255)]]}),this.messageTypeFormGroup.get("messageTypeAlias").valueChanges.pipe(Be(this.destroy$)).subscribe((e=>this.updateMessageTypeValue(e))),this.messageTypeFormGroup.valueChanges.pipe(Be(this.destroy$)).subscribe((()=>this.updateView()))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}writeValue(e){this.modelValue=e;let t=this.messageTypes.find((t=>t.value===e));t||(t=this.messageTypes.find((e=>""===e.value))),this.messageTypeFormGroup.get("messageTypeAlias").patchValue(t,{emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1})}validate(){return this.messageTypeFormGroup.valid?null:{messageTypeInvalid:!0}}setDisabledState(e){this.disabled=e,e?this.messageTypeFormGroup.disable({emitEvent:!1}):(this.messageTypeFormGroup.enable({emitEvent:!1}),"Custom"!==this.messageTypeFormGroup.get("messageTypeAlias").value?.name&&this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}))}updateView(){const e=this.messageTypeFormGroup.getRawValue().messageType;this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[O.required,O.maxLength(255)]:[O.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}updateMessageTypeValue(e){"Custom"!==e?.name?this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}):this.messageTypeFormGroup.get("messageType").enable({emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e.value??null)}}e("OutputMessageTypeAutocompleteComponent",Rn),Rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,deps:[{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Rn,selector:"tb-output-message-type-autocomplete",inputs:{subscriptSizing:"subscriptSizing",disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>Rn)),multi:!0},{provide:K,useExisting:a((()=>Rn)),multi:!0}],ngImport:t,template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:wn,selector:"[ngxClipboard]",inputs:["ngxClipboard","container","cbContent","cbSuccessMsg"],outputs:["cbOnSuccess","cbOnError"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],Rn.prototype,"disabled",void 0),Oe([T()],Rn.prototype,"required",null),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,decorators:[{type:n,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:B,useExisting:a((()=>Rn)),multi:!0},{provide:K,useExisting:a((()=>Rn)),multi:!0}],template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n'}]}],ctorParameters:function(){return[{type:R.FormBuilder}]},propDecorators:{subscriptSizing:[{type:i}],disabled:[{type:i}],required:[{type:i}]}});class On{constructor(e,t){this.fb=e,this.translate=t,this.translation=nn,this.propagateChange=()=>{},this.destroy$=new _e,this.selectOptions=[]}ngOnInit(){this.initOptions(),this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(De(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}initOptions(){for(const e of this.translation.keys())this.selectOptions.push({value:e,name:this.translate.instant(this.translation.get(e))})}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}}e("MsgMetadataChipComponent",On),On.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,deps:[{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),On.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:On,selector:"tb-msg-metadata-chip",inputs:{labelText:"labelText",translation:"translation"},providers:[{provide:B,useExisting:a((()=>On)),multi:!0}],ngImport:t,template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:be.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:be.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,decorators:[{type:n,args:[{selector:"tb-msg-metadata-chip",providers:[{provide:B,useExisting:a((()=>On)),multi:!0}],template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n'}]}],ctorParameters:function(){return[{type:R.FormBuilder},{type:Z.TranslateService}]},propDecorators:{labelText:[{type:i}],translation:[{type:i}]}});class _n extends L{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new _e,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof z||e instanceof U){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return ae(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(De(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[O.required]],value:[t.value,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)ie(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[O.required]],value:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(De(this.destroy$)).subscribe((t=>{const n=Lt.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("SvMapConfigComponent",_n),_n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),_n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_n,selector:"tb-sv-map-config",inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:B,useExisting:a((()=>_n)),multi:!0},{provide:K,useExisting:a((()=>_n)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Ie.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],_n.prototype,"disabled",void 0),Oe([T()],_n.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,decorators:[{type:n,args:[{selector:"tb-sv-map-config",providers:[{provide:B,useExisting:a((()=>_n)),multi:!0},{provide:K,useExisting:a((()=>_n)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{selectOptions:[{type:i}],disabled:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],targetKeyPrefix:[{type:i}],selectText:[{type:i}],selectRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class Bn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigOldComponent",Bn),Bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Bn,selector:"tb-relations-query-config-old",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>Bn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Qe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,decorators:[{type:n,args:[{selector:"tb-relations-query-config-old",providers:[{provide:B,useExisting:a((()=>Bn)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Kn{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new _e,this.separatorKeysCodes=[ge,ye,xe],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[[],[]],sharedAttributeNames:[[],[]],serverAttributeNames:[[],[]],latestTsKeyNames:[[],[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(O.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(De(this.destroy$)).subscribe((e=>{this.propagateChange(this.preparePropagateValue(e))}))}preparePropagateValue(e){const t={};for(const n in e)t[n]="getLatestValueWithTs"===n||ie(e[n])?e[n]:[];return t}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}}e("SelectAttributesComponent",Kn),Kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,deps:[{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Kn,selector:"tb-select-attributes",inputs:{popupHelpLink:"popupHelpLink"},providers:[{provide:B,useExisting:a((()=>Kn)),multi:!0},{provide:K,useExisting:Kn,multi:!0}],ngImport:t,template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,decorators:[{type:n,args:[{selector:"tb-select-attributes",providers:[{provide:B,useExisting:a((()=>Kn)),multi:!0},{provide:K,useExisting:Kn,multi:!0}],template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:Z.TranslateService},{type:R.FormBuilder}]},propDecorators:{popupHelpLink:[{type:i}]}});class zn extends L{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new _e,this.alarmStatus=q,this.alarmStatusTranslations=A}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(De(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}}e("AlarmStatusSelectComponent",zn),zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:zn,selector:"tb-alarm-status-select",providers:[{provide:B,useExisting:a((()=>zn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"],dependencies:[{kind:"component",type:be.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:be.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,decorators:[{type:n,args:[{selector:"tb-alarm-status-select",providers:[{provide:B,useExisting:a((()=>zn)),multi:!0}],template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Un{}e("RulenodeCoreConfigCommonModule",Un),Un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Un.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Un,declarations:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn],imports:[$,M,Re],exports:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn]}),Un.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,imports:[$,M,Re]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,decorators:[{type:c,args:[{declarations:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn],imports:[$,M,Re],exports:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn]}]}]});class Hn{}e("RuleNodeCoreConfigActionModule",Hn),Hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Hn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Hn,declarations:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In],imports:[$,M,Re,Un],exports:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In]}),Hn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,imports:[$,M,Re,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,decorators:[{type:c,args:[{declarations:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In],imports:[$,M,Re,Un],exports:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In]}]}]});class jn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ge,ye,xe]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[O.min(0),O.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]]})}prepareInputConfig(e){return{inputValueKey:ie(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:ie(e?.outputValueKey)?e.outputValueKey:null,useCache:!ie(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!ie(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:ie(e?.periodValueKey)?e.periodValueKey:null,round:ie(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!ie(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative}}prepareOutputConfig(e){return le(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([O.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",jn),jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:jn,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n",dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n"}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class $n extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Pt;for(const e of Rt.keys())e!==Pt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,le(e)}prepareInputConfig(e){let t,n;return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[O.required]],fetchTo:[e.fetchTo]})}}e("CustomerAttributesConfigComponent",$n),$n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),$n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:$n,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,decorators:[{type:n,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Qn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[O.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return se(e)&&(e.attributesControl={clientAttributeNames:ie(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:ie(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:ie(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!ie(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:ie(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!ie(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("DeviceAttributesConfigComponent",Qn),Qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Qn,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:qn,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Kn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Jn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(Mt))this.predefinedValues.push({value:Mt[e],name:this.translate.instant(Et.get(Mt[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=ie(e?.addToMetadata)?e.addToMetadata?en.METADATA:en.DATA:e?.fetchTo?e.fetchTo:en.DATA,{detailsList:ie(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[O.required]],fetchTo:[e.fetchTo,[]]})}}e("EntityDetailsConfigComponent",Jn),Jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Jn,selector:"tb-enrichment-node-entity-details-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Yn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ge,ye,xe],this.aggregationTypes=E,this.aggregations=Object.values(E),this.aggregationTypesTranslations=G,this.fetchMode=Gt,this.samplingOrders=Object.values(Vt),this.samplingOrdersTranslate=_t,this.timeUnits=Object.values(Nt),this.timeUnitsTranslationMap=St,this.deduplicationStrategiesHintTranslations=wt,this.headerOptions=[],this.timeUnitMap={[Nt.MILLISECONDS]:1,[Nt.SECONDS]:1e3,[Nt.MINUTES]:6e4,[Nt.HOURS]:36e5,[Nt.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Dt.keys())this.headerOptions.push({value:e,name:this.translate.instant(Dt.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[O.required]],aggregation:[e.aggregation,[O.required]],fetchMode:[e.fetchMode,[O.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,le(e)}prepareInputConfig(e){return se(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:ie(e?.aggregation)?e.aggregation:E.NONE,fetchMode:ie(e?.fetchMode)?e.fetchMode:Gt.FIRST,orderBy:ie(e?.orderBy)?e.orderBy:Vt.ASC,limit:ie(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!ie(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:ie(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:ie(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:Nt.MINUTES,endInterval:ie(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:ie(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:Nt.MINUTES},startIntervalPattern:ie(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:ie(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Gt.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([O.required,O.min(2),O.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([O.required,O.min(1),O.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([O.required,O.min(1),O.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===Gt.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===E.NONE}}e("GetTelemetryFromDatabaseConfigComponent",Yn),Yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Yn,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Wn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return se(e)&&(e.attributesControl={clientAttributeNames:ie(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:ie(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:ie(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!ie(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA,tellFailureIfAbsent:!!ie(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:ie(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("OriginatorAttributesConfigComponent",Wn),Wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Wn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Wn,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Kn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Zn extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of kt)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return le(e)}prepareInputConfig(e){return{dataMapping:ie(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:ie(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[O.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}}e("OriginatorFieldsConfigComponent",Zn),Zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Zn,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n',dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:_n,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Xn extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=Pt,this.msgMetadataLabelTranslations=Ot,this.originatorFields=[],this.fetchToData=[];for(const e of Object.keys(kt))this.originatorFields.push({value:kt[e].value,name:this.translate.instant(kt[e].name)});for(const e of Rt.keys())this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===Pt.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,le(e)}prepareInputConfig(e){let t,n,r={[k.name.value]:`relatedEntity${this.translate.instant(k.name.name)}`},o={serialNumber:"sn"};return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,t===Pt.FIELDS?r=n:o=n,{relationsQuery:ie(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:o,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[O.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[O.required]],svMap:[e.svMap,[O.required]],fetchTo:[e.fetchTo,[]]})}validatorTriggers(){return["dataToFetch"]}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===Pt.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}}e("RelatedAttributesConfigComponent",Xn),Xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Xn,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:An,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:_n,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class er extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Pt;for(const e of Rt.keys())e!==Pt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[O.required]],fetchTo:[e.fetchTo,[]]})}}e("TenantAttributesConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:er,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,decorators:[{type:n,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class tr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}}e("FetchDeviceCredentialsConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tr,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,decorators:[{type:n,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class nr{}e("RulenodeCoreConfigEnrichmentModule",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),nr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:nr,declarations:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr],imports:[$,M,Un],exports:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr]}),nr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,decorators:[{type:c,args:[{declarations:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr],imports:[$,M,Un],exports:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr]}]}]});class rr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Ht,this.azureIotHubCredentialsTypeTranslationsMap=jt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[O.required,O.min(1),O.max(200)]],clientId:[e?e.clientId:null,[O.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[O.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([O.required]);break;case"cert.PEM":t.get("privateKey").setValidators([O.required]),t.get("privateKeyFileName").setValidators([O.required]),t.get("cert").setValidators([O.required]),t.get("certFileName").setValidators([O.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rr,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:H.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Je.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Je.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,decorators:[{type:n,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class or extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Qt,this.ToByteStandartCharsetTypeTranslationMap=Jt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[O.required]],retries:[e?e.retries:null,[O.min(0)]],batchSize:[e?e.batchSize:null,[O.min(0)]],linger:[e?e.linger:null,[O.min(0)]],bufferMemory:[e?e.bufferMemory:null,[O.min(0)]],acks:[e?e.acks:null,[O.required]],keySerializer:[e?e.keySerializer:null,[O.required]],valueSerializer:[e?e.valueSerializer:null,[O.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([O.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:or,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,decorators:[{type:n,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ar extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[O.required,O.min(1),O.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&me(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){me(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}}e("MqttConfigComponent",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ar,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:En,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,decorators:[{type:n,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ir extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=D,this.entityType=F}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[O.required]],targets:[e?e.targets:[],[O.required]]})}}e("NotificationConfigComponent",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ir,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:tt.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:nt.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","allowEdit","disabled","notificationTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,decorators:[{type:n,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class lr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[O.required]],topicName:[e?e.topicName:null,[O.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[O.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[O.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:lr,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,decorators:[{type:n,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class sr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[O.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[O.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:sr,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,decorators:[{type:n,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class mr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys($t)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[O.required]],requestMethod:[e?e.requestMethod:null,[O.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[O.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,o=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!o?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[O.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[O.required,O.min(1),O.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([O.min(0)])),n?this.restApiCallConfigForm.get("maxQueueSize").setValidators([O.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:mr,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:En,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,decorators:[{type:n,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class pr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([O.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([O.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([O.required,O.min(1),O.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([O.required,O.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[O.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[O.required,O.min(1),O.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:pr,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:rt.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,decorators:[{type:n,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class dr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[O.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[O.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([O.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dr,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ot.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,decorators:[{type:n,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ur extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(w),this.slackChanelTypesTranslateMap=V}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[O.required]],conversationType:[e?e.conversationType:null,[O.required]],conversation:[e?e.conversation:null,[O.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([O.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ur.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ur,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:at.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:at.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:it.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,decorators:[{type:n,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[O.required]],accessKeyId:[e?e.accessKeyId:null,[O.required]],secretAccessKey:[e?e.secretAccessKey:null,[O.required]],region:[e?e.region:null,[O.required]]})}}e("SnsConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cr,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,decorators:[{type:n,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class fr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Bt,this.sqsQueueTypes=Object.keys(Bt),this.sqsQueueTypeTranslationsMap=Kt}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[O.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[O.required]],delaySeconds:[e?e.delaySeconds:null,[O.min(0),O.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[O.required]],secretAccessKey:[e?e.secretAccessKey:null,[O.required]],region:[e?e.region:null,[O.required]]})}}e("SqsConfigComponent",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fr,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,decorators:[{type:n,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class gr{}e("RulenodeCoreConfigExternalModule",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),gr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:gr,declarations:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur],imports:[$,M,Re,Un],exports:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur]}),gr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,imports:[$,M,Re,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,decorators:[{type:c,args:[{declarations:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur],imports:[$,M,Re,Un],exports:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur]}]}]});class yr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:ie(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[O.required]]})}}e("CheckAlarmStatusComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yr,selector:"tb-filter-node-check-alarm-status-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:zn,selector:"tb-alarm-status-select"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class xr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:ie(e?.messageNames)?e.messageNames:[],metadataNames:ie(e?.metadataNames)?e.metadataNames:[],checkAllKeys:!!ie(e?.checkAllKeys)&&e.checkAllKeys}}prepareOutputConfig(e){return{messageNames:ie(e?.messageNames)?e.messageNames:[],metadataNames:ie(e?.metadataNames)?e.metadataNames:[],checkAllKeys:e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(O.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}}e("CheckMessageConfigComponent",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xr,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-message-config",template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class br extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(v),this.entitySearchDirectionTranslationsMap=C}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!ie(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:ie(e?.direction)?e.direction:null,entityType:ie(e?.entityType)?e.entityType:null,entityId:ie(e?.entityId)?e.entityId:null,relationType:ie(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[O.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[O.required]:[]],relationType:[e.relationType,[O.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[O.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[O.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),br.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:br,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:lt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:$e.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,decorators:[{type:n,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class hr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Tt,this.perimeterTypes=Object.values(Tt),this.perimeterTypeTranslationMap=It,this.rangeUnits=Object.values(qt),this.rangeUnitTranslationMap=At,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:ie(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:ie(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:ie(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!ie(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:ie(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:ie(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:ie(e?.centerLongitude)?e.centerLongitude:null,range:ie(e?.range)?e.range:null,rangeUnit:ie(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:ie(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[O.required]],longitudeKeyName:[e.longitudeKeyName,[O.required]],perimeterType:[e.perimeterType,[O.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([O.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==Tt.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([O.required,O.min(-90),O.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([O.required,O.min(-180),O.max(180)]),this.geoFilterConfigForm.get("range").setValidators([O.required,O.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([O.required]),this.defaultPaddingEnable=!1),t||n!==Tt.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([O.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",hr),hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,decorators:[{type:n,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class vr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:ie(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[O.required]]})}}e("MessageTypeConfigComponent",vr),vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Mn,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,decorators:[{type:n,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[F.DEVICE,F.ASSET,F.ENTITY_VIEW,F.TENANT,F.CUSTOMER,F.USER,F.DASHBOARD,F.RULE_CHAIN,F.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:ie(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[O.required]]})}}e("OriginatorTypeConfigComponent",Cr),Cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:st.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","additionalClasses","appearance","label","floatLabel","disabled","subscriptSizing","allowedEntityTypes","emptyInputPlaceholder","filledInputPlaceholder","ignoreAuthorityFilter"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,decorators:[{type:n,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Fr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[O.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),{scriptLang:ie(e?.scriptLang)?e.scriptLang:x.JS,jsScript:ie(e?.jsScript)?e.jsScript:null,tbelScript:ie(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Fr),Fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fr,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,decorators:[{type:n,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class kr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[O.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),{scriptLang:ie(e?.scriptLang)?e.scriptLang:x.JS,jsScript:ie(e?.jsScript)?e.jsScript:null,tbelScript:ie(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",o=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",kr),kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kr,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,decorators:[{type:n,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Lr{}e("RuleNodeCoreConfigFilterModule",Lr),Lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Lr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Lr,declarations:[xr,br,hr,vr,Cr,Fr,kr,yr],imports:[$,M,Un],exports:[xr,br,hr,vr,Cr,Fr,kr,yr]}),Lr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,decorators:[{type:c,args:[{declarations:[xr,br,hr,vr,Cr,Fr,kr,yr],imports:[$,M,Un],exports:[xr,br,hr,vr,Cr,Fr,kr,yr]}]}]});class Tr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=vt,this.originatorSources=Object.keys(vt),this.originatorSourceTranslationMap=Ct,this.originatorSourceDescTranslationMap=Ft,this.allowedEntityTypes=[F.DEVICE,F.ASSET,F.ENTITY_VIEW,F.USER,F.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[O.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===vt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([O.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===vt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([O.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Tr),Tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tr,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ne.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:Ne.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:An,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,decorators:[{type:n,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Ir extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[O.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",Ir),Ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,deps:[{token:P.Store},{token:R.FormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ir,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,decorators:[{type:n,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}); - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - const Nr=mt({passive:!0});class Sr{constructor(e,t){this._platform=e,this._ngZone=t,this._monitoredElements=new Map}monitor(e){if(!this._platform.isBrowser)return ze;const t=ke(e),n=this._monitoredElements.get(t);if(n)return n.subject;const r=new _e,o="cdk-text-field-autofilled",a=e=>{"cdk-text-field-autofill-start"!==e.animationName||t.classList.contains(o)?"cdk-text-field-autofill-end"===e.animationName&&t.classList.contains(o)&&(t.classList.remove(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!1})))):(t.classList.add(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!0}))))};return this._ngZone.runOutsideAngular((()=>{t.addEventListener("animationstart",a,Nr),t.classList.add("cdk-text-field-autofill-monitored")})),this._monitoredElements.set(t,{subject:r,unlisten:()=>{t.removeEventListener("animationstart",a,Nr)}}),r}stopMonitoring(e){const t=ke(e),n=this._monitoredElements.get(t);n&&(n.unlisten(),n.subject.complete(),t.classList.remove("cdk-text-field-autofill-monitored"),t.classList.remove("cdk-text-field-autofilled"),this._monitoredElements.delete(t))}ngOnDestroy(){this._monitoredElements.forEach(((e,t)=>this.stopMonitoring(t)))}}Sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,deps:[{token:pt.Platform},{token:t.NgZone}],target:t.ɵɵFactoryTarget.Injectable}),Sr.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:pt.Platform},{type:t.NgZone}]}});class qr{constructor(e,t){this._elementRef=e,this._autofillMonitor=t,this.cdkAutofill=new r}ngOnInit(){this._autofillMonitor.monitor(this._elementRef).subscribe((e=>this.cdkAutofill.emit(e)))}ngOnDestroy(){this._autofillMonitor.stopMonitoring(this._elementRef)}}qr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:qr,deps:[{token:t.ElementRef},{token:Sr}],target:t.ɵɵFactoryTarget.Directive}),qr.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:qr,selector:"[cdkAutofill]",outputs:{cdkAutofill:"cdkAutofill"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:qr,decorators:[{type:d,args:[{selector:"[cdkAutofill]"}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:Sr}]},propDecorators:{cdkAutofill:[{type:u}]}}); - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - class Ar{get minRows(){return this._minRows}set minRows(e){this._minRows=Le(e),this._setMinHeight()}get maxRows(){return this._maxRows}set maxRows(e){this._maxRows=Le(e),this._setMaxHeight()}get enabled(){return this._enabled}set enabled(e){e=Fe(e),this._enabled!==e&&((this._enabled=e)?this.resizeToFitContent(!0):this.reset())}get placeholder(){return this._textareaElement.placeholder}set placeholder(e){this._cachedPlaceholderHeight=void 0,e?this._textareaElement.setAttribute("placeholder",e):this._textareaElement.removeAttribute("placeholder"),this._cacheTextareaPlaceholderHeight()}constructor(e,t,n,r){this._elementRef=e,this._platform=t,this._ngZone=n,this._destroyed=new _e,this._enabled=!0,this._previousMinRows=-1,this._isViewInited=!1,this._handleFocusEvent=e=>{this._hasFocus="focus"===e.type},this._document=r,this._textareaElement=this._elementRef.nativeElement}_setMinHeight(){const e=this.minRows&&this._cachedLineHeight?this.minRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.minHeight=e)}_setMaxHeight(){const e=this.maxRows&&this._cachedLineHeight?this.maxRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.maxHeight=e)}ngAfterViewInit(){this._platform.isBrowser&&(this._initialHeight=this._textareaElement.style.height,this.resizeToFitContent(),this._ngZone.runOutsideAngular((()=>{const e=this._getWindow();Ue(e,"resize").pipe(we(16),De(this._destroyed)).subscribe((()=>this.resizeToFitContent(!0))),this._textareaElement.addEventListener("focus",this._handleFocusEvent),this._textareaElement.addEventListener("blur",this._handleFocusEvent)})),this._isViewInited=!0,this.resizeToFitContent(!0))}ngOnDestroy(){this._textareaElement.removeEventListener("focus",this._handleFocusEvent),this._textareaElement.removeEventListener("blur",this._handleFocusEvent),this._destroyed.next(),this._destroyed.complete()}_cacheTextareaLineHeight(){if(this._cachedLineHeight)return;let e=this._textareaElement.cloneNode(!1);e.rows=1,e.style.position="absolute",e.style.visibility="hidden",e.style.border="none",e.style.padding="0",e.style.height="",e.style.minHeight="",e.style.maxHeight="",e.style.overflow="hidden",this._textareaElement.parentNode.appendChild(e),this._cachedLineHeight=e.clientHeight,e.remove(),this._setMinHeight(),this._setMaxHeight()}_measureScrollHeight(){const e=this._textareaElement,t=e.style.marginBottom||"",n=this._platform.FIREFOX,r=n&&this._hasFocus,o=n?"cdk-textarea-autosize-measuring-firefox":"cdk-textarea-autosize-measuring";r&&(e.style.marginBottom=`${e.clientHeight}px`),e.classList.add(o);const a=e.scrollHeight-4;return e.classList.remove(o),r&&(e.style.marginBottom=t),a}_cacheTextareaPlaceholderHeight(){if(!this._isViewInited||null!=this._cachedPlaceholderHeight)return;if(!this.placeholder)return void(this._cachedPlaceholderHeight=0);const e=this._textareaElement.value;this._textareaElement.value=this._textareaElement.placeholder,this._cachedPlaceholderHeight=this._measureScrollHeight(),this._textareaElement.value=e}ngDoCheck(){this._platform.isBrowser&&this.resizeToFitContent()}resizeToFitContent(e=!1){if(!this._enabled)return;if(this._cacheTextareaLineHeight(),this._cacheTextareaPlaceholderHeight(),!this._cachedLineHeight)return;const t=this._elementRef.nativeElement,n=t.value;if(!e&&this._minRows===this._previousMinRows&&n===this._previousValue)return;const r=this._measureScrollHeight(),o=Math.max(r,this._cachedPlaceholderHeight||0);t.style.height=`${o}px`,this._ngZone.runOutsideAngular((()=>{"undefined"!=typeof requestAnimationFrame?requestAnimationFrame((()=>this._scrollToCaretPosition(t))):setTimeout((()=>this._scrollToCaretPosition(t)))})),this._previousValue=n,this._previousMinRows=this._minRows}reset(){void 0!==this._initialHeight&&(this._textareaElement.style.height=this._initialHeight)}_noopInputHandler(){}_getDocument(){return this._document||document}_getWindow(){return this._getDocument().defaultView||window}_scrollToCaretPosition(e){const{selectionStart:t,selectionEnd:n}=e;!this._destroyed.isStopped&&this._hasFocus&&e.setSelectionRange(t,n)}}Ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Ar,deps:[{token:t.ElementRef},{token:pt.Platform},{token:t.NgZone},{token:j,optional:!0}],target:t.ɵɵFactoryTarget.Directive}),Ar.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:Ar,selector:"textarea[cdkTextareaAutosize]",inputs:{minRows:["cdkAutosizeMinRows","minRows"],maxRows:["cdkAutosizeMaxRows","maxRows"],enabled:["cdkTextareaAutosize","enabled"],placeholder:"placeholder"},host:{attributes:{rows:"1"},listeners:{input:"_noopInputHandler()"},classAttribute:"cdk-textarea-autosize"},exportAs:["cdkTextareaAutosize"],ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Ar,decorators:[{type:d,args:[{selector:"textarea[cdkTextareaAutosize]",exportAs:"cdkTextareaAutosize",host:{class:"cdk-textarea-autosize",rows:"1","(input)":"_noopInputHandler()"}}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:pt.Platform},{type:t.NgZone},{type:void 0,decorators:[{type:p},{type:m,args:[j]}]}]},propDecorators:{minRows:[{type:i,args:["cdkAutosizeMinRows"]}],maxRows:[{type:i,args:["cdkAutosizeMaxRows"]}],enabled:[{type:i,args:["cdkTextareaAutosize"]}],placeholder:[{type:i}]}}); - /** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - class Mr{}Mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Mr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,declarations:[qr,Ar],exports:[qr,Ar]}),Mr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,decorators:[{type:c,args:[{declarations:[qr,Ar],exports:[qr,Ar]}]}]});class Er extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",description:"tb.mail-body-type.plain-text-description",value:"false"},{name:"tb.mail-body-type.html",description:"tb.mail-body-type.html-text-description",value:"true"},{name:"tb.mail-body-type.use-body-type-template",description:"tb.mail-body-type.dynamic-text-description",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[O.required]],toTemplate:[e?e.toTemplate:null,[O.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[O.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[O.required]],bodyTemplate:[e?e.bodyTemplate:null,[O.required]]})}prepareInputConfig(e){return{fromTemplate:ie(e?.fromTemplate)?e.fromTemplate:null,toTemplate:ie(e?.toTemplate)?e.toTemplate:null,ccTemplate:ie(e?.ccTemplate)?e.ccTemplate:null,bccTemplate:ie(e?.bccTemplate)?e.bccTemplate:null,subjectTemplate:ie(e?.subjectTemplate)?e.subjectTemplate:null,mailBodyType:ie(e?.mailBodyType)?e.mailBodyType:null,isHtmlTemplate:ie(e?.isHtmlTemplate)?e.isHtmlTemplate:null,bodyTemplate:ie(e?.bodyTemplate)?e.bodyTemplate:null}}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}getBodyTypeName(){return this.mailBodyTypes.find((e=>e.value===this.toEmailConfigForm.get("mailBodyType").value)).name}}e("ToEmailConfigComponent",Er),Er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Er,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Er,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Ar,selector:"textarea[cdkTextareaAutosize]",inputs:["cdkAutosizeMinRows","cdkAutosizeMaxRows","cdkTextareaAutosize","placeholder"],exportAs:["cdkTextareaAutosize"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ne.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:Ne.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Er,decorators:[{type:n,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Gr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.copyFrom=[],this.translation=tn;for(const e of this.translation.keys())this.copyFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({copyFrom:[e.copyFrom,[O.required]],keys:[e?e.keys:null,[O.required]]})}configForm(){return this.copyKeysConfigForm}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.copyFrom?en.METADATA:en.DATA:ie(e?.copyFrom)?e.copyFrom:en.DATA,{keys:ie(e?.keys)?e.keys:null,copyFrom:t}}}e("CopyKeysConfigComponent",Gr),Gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Gr,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gr,decorators:[{type:n,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Dr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.renameIn=[],this.translation=rn;for(const e of this.translation.keys())this.renameIn.push({value:e,name:this.translate.instant(this.translation.get(e))})}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({renameIn:[e?e.renameIn:null,[O.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[O.required]]})}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.fromMetadata?en.METADATA:en.DATA:ie(e?.renameIn)?e?.renameIn:en.DATA,{renameKeysMapping:ie(e?.renameKeysMapping)?e.renameKeysMapping:null,renameIn:t}}}e("RenameKeysConfigComponent",Dr),Dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Dr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dr,decorators:[{type:n,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class wr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[O.required]]})}}e("NodeJsonPathConfigComponent",wr),wr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:wr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n",dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,decorators:[{type:n,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n"}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Vr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.deleteFrom=[],this.translation=nn;for(const e of this.translation.keys())this.deleteFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({deleteFrom:[e.deleteFrom,[O.required]],keys:[e?e.keys:null,[O.required]]})}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.fromMetadata?en.METADATA:en.DATA:ie(e?.deleteFrom)?e?.deleteFrom:en.DATA,{keys:ie(e?.keys)?e.keys:null,deleteFrom:t}}configForm(){return this.deleteKeysConfigForm}}e("DeleteKeysConfigComponent",Vr),Vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Vr,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,decorators:[{type:n,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Pr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.deduplicationStrategie=Gt,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Dt}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[ie(e?.interval)?e.interval:null,[O.required,O.min(1)]],strategy:[ie(e?.strategy)?e.strategy:null,[O.required]],outMsgType:[ie(e?.outMsgType)?e.outMsgType:null,[O.required]],maxPendingMsgs:[ie(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[O.required,O.min(1),O.max(1e3)]],maxRetries:[ie(e?.maxRetries)?e.maxRetries:null,[O.required,O.min(0),O.max(100)]]})}prepareInputConfig(e){return e||(e={}),e.outMsgType||(e.outMsgType="POST_TELEMETRY_REQUEST"),super.prepareInputConfig(e)}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}):this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}}e("DeduplicationConfigComponent",Pr),Pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Pr,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Rn,selector:"tb-output-message-type-autocomplete",inputs:["subscriptSizing","disabled","required"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,decorators:[{type:n,args:[{selector:"tb-action-node-msg-deduplication-config",template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Rr{}e("RulenodeCoreConfigTransformModule",Rr),Rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Rr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Rr,declarations:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr],imports:[$,M,Un],exports:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr]}),Rr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,decorators:[{type:c,args:[{declarations:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr],imports:[$,M,Un],exports:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr]}]}]});class Or extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=F}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[O.required]]})}}e("RuleChainInputComponent",Or),Or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Or,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:lt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class _r extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",_r),_r.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_r.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_r,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Br{}e("RuleNodeCoreConfigFlowModule",Br),Br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Br,declarations:[Or,_r],imports:[$,M,Un],exports:[Or,_r]}),Br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,decorators:[{type:c,args:[{declarations:[Or,_r],imports:[$,M,Un],exports:[Or,_r]}]}]});class Kr{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","advanced-settings":"Advanced settings","create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","copy-message-type":"Copy message type","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","message-type-value":"Message type value","message-type-value-required":"Message type value is required","message-type-value-max-length":"Message type value should be less than 256","output-message-type":"Output message type","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Force notification to the device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-on-update-hint":"If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.","notify-device-on-delete-hint":"If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.","latest-timeseries":"Latest time-series data keys","timeseries-keys":"Timeseries keys","timeseries-keys-required":"At least one timeseries key should be selected.","add-timeseries-key":"Add timeseries key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","current-key-name":"Current key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","relations-query-config-direction-suffix":"originator","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","new-originator":"New originator","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related entity","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity by name pattern","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From","from-template-required":"From is required","message-to-metadata":"Message to metadata","metadata-to-message":"Metadata to message","from-message":"From message","from-metadata":"From metadata","to-template":"To","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc","bcc-template":"Bcc","subject-template":"Subject","subject-template-required":"Subject Template is required","body-template":"Body","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","body-type-template":"Body type template","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","email-sender":"Email sender","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time-series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","add-device-profile":"Add device profile","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"timeseries key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch timeseries from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch timeseries invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required","keys-mapping":"keys mapping","add-key":"Add key",recipients:"Recipients","message-subject-and-content":"Message subject and content","template-rules-hint":"Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","originator-customer-desc":"Use customer of incoming message originator as new originator.","originator-tenant-desc":"Use current tenant as new originator.","originator-related-entity-desc":"Use related entity as new originator. Lookup based on configured relation type and direction.","originator-alarm-originator-desc":"Use alarm originator as new originator. Only if incoming message originator is alarm entity.","originator-entity-by-name-pattern-desc":"Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.","email-from-template-hint":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","recipients-block-main-hint":"Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata."},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","copy-key-values-from":"Copy key-values from","delete-key-values":"Delete key-values","delete-key-values-from":"Delete key-values from","at-least-one-key-error":"At least one key should be selected.","unique-key-value-pair-error":"'{{keyText}}' must be different from the '{{valText}}'!"},"mail-body-type":{"plain-text":"Plain text",html:"HTML",dynamic:"Dynamic","use-body-type-template":"Use body type template","plain-text-description":"Simple, unformatted text with no special styling or formating.","html-text-description":"Allows you to use HTML tags for formatting, links and images in your mai body.","dynamic-text-description":"Allows to use Plain Text or HTML body type dynamically based on templatization feature.","after-template-evaluation-hint":"After template evaluation value should be true for HTML, and false for Plain text."}}},!0)}(e)}}e("RuleNodeCoreConfigModule",Kr),Kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,deps:[{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),Kr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Kr,declarations:[dt],imports:[$,M],exports:[Hn,Lr,nr,gr,Rr,Br,dt]}),Kr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,imports:[$,M,Hn,Lr,nr,gr,Rr,Br]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,decorators:[{type:c,args:[{declarations:[dt],imports:[$,M],exports:[Hn,Lr,nr,gr,Rr,Br,dt]}]}],ctorParameters:function(){return[{type:Z.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map +System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/common","@angular/material/checkbox","@angular/material/input","@angular/material/form-field","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/select","@angular/material/core","@angular/material/slide-toggle","@shared/components/hint-tooltip-icon.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/material/icon","@angular/material/tooltip","@shared/components/script-lang.component","@angular/cdk/keycodes","@angular/material/chips","@shared/pipe/safe.pipe","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/flex-layout/extended","@angular/material/list","@angular/cdk/drag-drop","rxjs/operators","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@home/components/public-api","tslib","rxjs","@shared/components/help-popup.component","@shared/components/entity/entity-subtype-list.component","@shared/components/relation/relation-type-autocomplete.component","@home/components/relation/relation-filters.component","@angular/material/expansion","@shared/components/file-input.component","@shared/components/button/toggle-password.component","@shared/components/string-items-list.component","@shared/components/toggle-header.component","@shared/components/toggle-select.component","@shared/components/entity/entity-list.component","@shared/components/notification/template-autocomplete.component","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@angular/material/radio","@shared/components/slack-conversation-autocomplete.component","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component","@angular/cdk/platform"],(function(e){"use strict";var t,n,r,o,a,i,l,s,m,p,d,u,c,f,g,y,x,b,h,v,C,F,k,L,T,I,N,S,q,A,M,E,G,D,w,V,P,R,O,_,B,K,z,U,H,j,$,Q,J,Y,W,Z,X,ee,te,ne,re,oe,ae,ie,le,se,me,pe,de,ue,ce,fe,ge,ye,xe,be,he,ve,Ce,Fe,ke,Le,Te,Ie,Ne,Se,qe,Ae,Me,Ee,Ge,De,we,Ve,Pe,Re,Oe,_e,Be,Ke,ze,Ue,He,je,$e,Qe,Je,Ye,We,Ze,Xe,et,tt,nt,rt,ot,at,it,lt,st,mt,pt;return{setters:[function(e){t=e,n=e.Component,r=e.EventEmitter,o=e.ViewChild,a=e.forwardRef,i=e.Input,l=e.InjectionToken,s=e.Injectable,m=e.Inject,p=e.Optional,d=e.Directive,u=e.Output,c=e.NgModule},function(e){f=e.RuleNodeConfigurationComponent,g=e.AttributeScope,y=e.telemetryTypeTranslations,x=e.ScriptLanguage,b=e.AlarmSeverity,h=e.alarmSeverityTranslations,v=e.EntitySearchDirection,C=e.entitySearchDirectionTranslations,F=e.EntityType,k=e.entityFields,L=e.PageComponent,T=e.coerceBoolean,I=e.MessageType,N=e.messageTypeNames,S=e,q=e.AlarmStatus,A=e.alarmStatusTranslations,M=e.SharedModule,E=e.AggregationType,G=e.aggregationTranslations,D=e.NotificationType,w=e.SlackChanelType,V=e.SlackChanelTypesTranslateMap},function(e){P=e},function(e){R=e,O=e.Validators,_=e.NgControl,B=e.NG_VALUE_ACCESSOR,K=e.NG_VALIDATORS,z=e.FormArray,U=e.FormGroup},function(e){H=e,j=e.DOCUMENT,$=e.CommonModule},function(e){Q=e},function(e){J=e},function(e){Y=e},function(e){W=e},function(e){Z=e},function(e){X=e},function(e){ee=e},function(e){te=e},function(e){ne=e},function(e){re=e.getCurrentAuthState,oe=e,ae=e.isEqual,ie=e.isDefinedAndNotNull,le=e.deepTrim,se=e.isObject,me=e.isNotEmptyStr},function(e){pe=e},function(e){de=e},function(e){ue=e},function(e){ce=e},function(e){fe=e},function(e){ge=e.ENTER,ye=e.COMMA,xe=e.SEMICOLON},function(e){be=e},function(e){he=e},function(e){ve=e},function(e){Ce=e},function(e){Fe=e.coerceBooleanProperty,ke=e.coerceElement,Le=e.coerceNumberProperty},function(e){Te=e},function(e){Ie=e},function(e){Ne=e},function(e){Se=e},function(e){qe=e.tap,Ae=e.map,Me=e.startWith,Ee=e.mergeMap,Ge=e.share,De=e.takeUntil,we=e.auditTime},function(e){Ve=e},function(e){Pe=e},function(e){Re=e.HomeComponentsModule},function(e){Oe=e.__decorate},function(e){_e=e.Subject,Be=e.takeUntil,Ke=e.of,ze=e.EMPTY,Ue=e.fromEvent},function(e){He=e},function(e){je=e},function(e){$e=e},function(e){Qe=e},function(e){Je=e},function(e){Ye=e},function(e){We=e},function(e){Ze=e},function(e){Xe=e},function(e){et=e},function(e){tt=e},function(e){nt=e},function(e){rt=e},function(e){ot=e},function(e){at=e},function(e){it=e},function(e){lt=e},function(e){st=e},function(e){mt=e.normalizePassiveListenerOptions,pt=e}],execute:function(){class dt extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",dt),dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dt,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dt,decorators:[{type:n,args:[{selector:"tb-node-empty-config",template:"
"}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ut extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[O.required,O.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[O.required,O.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",ut),ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ut,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ut,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ut,decorators:[{type:n,args:[{selector:"tb-action-node-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ct extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]],updateAttributesOnlyOnValueChange:[!!e&&e.updateAttributesOnlyOnValueChange,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===g.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1}),this.attributesConfigForm.get("updateAttributesOnlyOnValueChange").patchValue(!1,{emitEvent:!1})}))}}e("AttributesConfigComponent",ct),ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ct,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,decorators:[{type:n,args:[{selector:"tb-action-node-attributes-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ft extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[O.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===x.JS?[O.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===x.TBEL?[O.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.clearAlarmConfigForm.get("scriptLang").value,n=t===x.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===x.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",o=this.clearAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.clearAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",ft),ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ft,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,decorators:[{type:n,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class gt extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.alarmSeverities=Object.keys(b),this.alarmSeverityTranslationMap=h,this.separatorKeysCodes=[ge,ye,xe],this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([O.required]),this.createAlarmConfigForm.get("severity").setValidators([O.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==x.TBEL||this.tbelEnabled||(r=x.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const o=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(o&&r===x.JS?[O.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(o&&r===x.TBEL?[O.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===x.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===x.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",o=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",gt),gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gt,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,decorators:[{type:n,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class yt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.entityType=F}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[O.required]],entityType:[e?e.entityType:null,[O.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[O.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[O.required,O.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==F.DEVICE&&t!==F.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([O.required,O.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",yt),yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yt,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,decorators:[{type:n,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class xt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.entityType=F}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[O.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[O.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[O.required,O.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([O.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",xt),xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xt,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,decorators:[{type:n,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class bt extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,O.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,O.required]})}}e("DeviceProfileConfigComponent",bt),bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bt,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,decorators:[{type:n,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ht extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function"}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[O.required,O.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[O.required,O.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",o=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}var vt;e("GeneratorConfigComponent",ht),ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ht,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Ce.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,decorators:[{type:n,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(vt||(vt={}));const Ct=new Map([[vt.CUSTOMER,"tb.rulenode.originator-customer"],[vt.TENANT,"tb.rulenode.originator-tenant"],[vt.RELATED,"tb.rulenode.originator-related"],[vt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[vt.ENTITY,"tb.rulenode.originator-entity"]]),Ft=new Map([[vt.CUSTOMER,"tb.rulenode.originator-customer-desc"],[vt.TENANT,"tb.rulenode.originator-tenant-desc"],[vt.RELATED,"tb.rulenode.originator-related-entity-desc"],[vt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator-desc"],[vt.ENTITY,"tb.rulenode.originator-entity-by-name-pattern-desc"]]),kt=[k.createdTime,k.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},k.firstName,k.lastName,k.email,k.title,k.country,k.state,k.city,k.address,k.address2,k.zip,k.phone,k.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],Lt=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var Tt;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(Tt||(Tt={}));const It=new Map([[Tt.CIRCLE,"tb.rulenode.perimeter-circle"],[Tt.POLYGON,"tb.rulenode.perimeter-polygon"]]);var Nt;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(Nt||(Nt={}));const St=new Map([[Nt.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[Nt.SECONDS,"tb.rulenode.time-unit-seconds"],[Nt.MINUTES,"tb.rulenode.time-unit-minutes"],[Nt.HOURS,"tb.rulenode.time-unit-hours"],[Nt.DAYS,"tb.rulenode.time-unit-days"]]);var qt;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(qt||(qt={}));const At=new Map([[qt.METER,"tb.rulenode.range-unit-meter"],[qt.KILOMETER,"tb.rulenode.range-unit-kilometer"],[qt.FOOT,"tb.rulenode.range-unit-foot"],[qt.MILE,"tb.rulenode.range-unit-mile"],[qt.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var Mt;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(Mt||(Mt={}));const Et=new Map([[Mt.ID,"tb.rulenode.entity-details-id"],[Mt.TITLE,"tb.rulenode.entity-details-title"],[Mt.COUNTRY,"tb.rulenode.entity-details-country"],[Mt.STATE,"tb.rulenode.entity-details-state"],[Mt.CITY,"tb.rulenode.entity-details-city"],[Mt.ZIP,"tb.rulenode.entity-details-zip"],[Mt.ADDRESS,"tb.rulenode.entity-details-address"],[Mt.ADDRESS2,"tb.rulenode.entity-details-address2"],[Mt.PHONE,"tb.rulenode.entity-details-phone"],[Mt.EMAIL,"tb.rulenode.entity-details-email"],[Mt.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var Gt;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(Gt||(Gt={}));const Dt=new Map([[Gt.FIRST,"tb.rulenode.first"],[Gt.LAST,"tb.rulenode.last"],[Gt.ALL,"tb.rulenode.all"]]),wt=new Map([[Gt.FIRST,"tb.rulenode.first-mode-hint"],[Gt.LAST,"tb.rulenode.last-mode-hint"],[Gt.ALL,"tb.rulenode.all-mode-hint"]]);var Vt,Pt;!function(e){e.ASC="ASC",e.DESC="DESC"}(Vt||(Vt={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(Pt||(Pt={}));const Rt=new Map([[Pt.ATTRIBUTES,"tb.rulenode.attributes"],[Pt.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[Pt.FIELDS,"tb.rulenode.fields"]]),Ot=new Map([[Pt.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[Pt.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[Pt.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),_t=new Map([[Vt.ASC,"tb.rulenode.ascending"],[Vt.DESC,"tb.rulenode.descending"]]);var Bt;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Bt||(Bt={}));const Kt=new Map([[Bt.STANDARD,"tb.rulenode.sqs-queue-standard"],[Bt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),zt=["anonymous","basic","cert.PEM"],Ut=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Ht=["sas","cert.PEM"],jt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var $t;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}($t||($t={}));const Qt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],Jt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Yt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Yt||(Yt={}));const Wt=new Map([[Yt.CUSTOM,{value:Yt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Yt.ADD,{value:Yt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Yt.SUB,{value:Yt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Yt.MULT,{value:Yt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Yt.DIV,{value:Yt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Yt.SIN,{value:Yt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Yt.SINH,{value:Yt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Yt.COS,{value:Yt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Yt.COSH,{value:Yt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Yt.TAN,{value:Yt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Yt.TANH,{value:Yt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Yt.ACOS,{value:Yt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Yt.ASIN,{value:Yt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Yt.ATAN,{value:Yt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Yt.ATAN2,{value:Yt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Yt.EXP,{value:Yt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Yt.EXPM1,{value:Yt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Yt.SQRT,{value:Yt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Yt.CBRT,{value:Yt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Yt.GET_EXP,{value:Yt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Yt.HYPOT,{value:Yt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Yt.LOG,{value:Yt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Yt.LOG10,{value:Yt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Yt.LOG1P,{value:Yt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Yt.CEIL,{value:Yt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Yt.FLOOR,{value:Yt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Yt.FLOOR_DIV,{value:Yt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Yt.FLOOR_MOD,{value:Yt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Yt.ABS,{value:Yt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Yt.MIN,{value:Yt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Yt.MAX,{value:Yt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Yt.POW,{value:Yt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Yt.SIGNUM,{value:Yt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Yt.RAD,{value:Yt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Yt.DEG,{value:Yt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var Zt,Xt,en;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(Zt||(Zt={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(Xt||(Xt={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(en||(en={}));const tn=new Map([[en.DATA,"tb.rulenode.message-to-metadata"],[en.METADATA,"tb.rulenode.metadata-to-message"]]),nn=(new Map([[en.DATA,"tb.rulenode.from-message"],[en.METADATA,"tb.rulenode.from-metadata"]]),new Map([[en.DATA,"tb.rulenode.message"],[en.METADATA,"tb.rulenode.metadata"]])),rn=new Map([[en.DATA,"tb.rulenode.message"],[en.METADATA,"tb.rulenode.message-metadata"]]),on=new Map([[Zt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[Zt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[Zt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[Zt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[Zt.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),an=new Map([[Xt.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[Xt.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[Xt.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[Xt.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),ln=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var sn,mn;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(sn||(sn={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}(mn||(mn={}));const pn=new Map([[sn.SHARED_SCOPE,"tb.rulenode.shared-scope"],[sn.SERVER_SCOPE,"tb.rulenode.server-scope"],[sn.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class dn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Tt,this.perimeterTypes=Object.keys(Tt),this.perimeterTypeTranslationMap=It,this.rangeUnits=Object.keys(qt),this.rangeUnitTranslationMap=At,this.timeUnits=Object.keys(Nt),this.timeUnitsTranslationMap=St}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[O.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[O.required]],perimeterType:[e?e.perimeterType:null,[O.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[O.required,O.min(1),O.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[O.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[O.required,O.min(1),O.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[O.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([O.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==Tt.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([O.required,O.min(-90),O.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([O.required,O.min(-180),O.max(180)]),this.geoActionConfigForm.get("range").setValidators([O.required,O.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([O.required])),t||n!==Tt.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([O.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",dn),dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dn,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dn,decorators:[{type:n,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class un extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",o=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",un),un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:un,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:un,decorators:[{type:n,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class cn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[O.required,O.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[O.required]]})}}e("MsgCountConfigComponent",cn),cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cn,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class fn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[O.required,O.min(1),O.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([O.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([O.required,O.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",fn),fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fn,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class gn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]]})}}e("PushToCloudConfigComponent",gn),gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gn,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class yn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]]})}}e("PushToEdgeConfigComponent",yn),yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yn,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class xn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",xn),xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xn,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class bn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[O.required,O.min(0)]]})}}e("RpcRequestConfigComponent",bn),bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bn,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class hn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[O.required]],value:[e[n],[O.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[O.required]],value:["",[O.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigOldComponent",hn),hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hn,selector:"tb-kv-map-config-old",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:B,useExisting:a((()=>hn)),multi:!0},{provide:K,useExisting:a((()=>hn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .header .tb-required:after{color:#757575;font-size:12px;font-weight:700}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Te.TbErrorComponent,selector:"tb-error",inputs:["noMargin","error"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Ie.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,decorators:[{type:n,args:[{selector:"tb-kv-map-config-old",providers:[{provide:B,useExisting:a((()=>hn)),multi:!0},{provide:K,useExisting:a((()=>hn)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .header .tb-required:after{color:#757575;font-size:12px;font-weight:700}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class vn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[O.required,O.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[O.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",vn),vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vn,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,decorators:[{type:n,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Cn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[O.required,O.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",Cn),Cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cn,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,decorators:[{type:n,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Fn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[O.required,O.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[O.required,O.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Fn),Fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fn,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,decorators:[{type:n,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class kn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y,this.separatorKeysCodes=[ge,ye,xe]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[O.required]],keys:[e?e.keys:null,[O.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",kn),kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kn,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,decorators:[{type:n,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:o,args:["attributeChipList"]}]}});class Ln extends L{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t){super(e),this.store=e,this.fb=t,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=Wt,this.ArgumentType=Zt,this.attributeScopeMap=pn,this.argumentTypeMap=on,this.arguments=Object.values(Zt),this.attributeScope=Object.values(sn),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray,n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}get argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormArray.controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormArray.removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormArray,n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Yt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([O.minLength(this.minArgs),O.maxLength(this.maxArgs)]);this.argumentsFormArray.length>this.maxArgs;)this.removeArgument(this.maxArgs-1);for(;this.argumentsFormArray.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===Zt.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==Zt.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormArray.controls.forEach(((e,t)=>{e.get("name").setValue(ln[t])}))}updateModel(){const e=this.argumentsFormArray.value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",Ln),Ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ln,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:B,useExisting:a((()=>Ln)),multi:!0},{provide:K,useExisting:a((()=>Ln)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"],dependencies:[{kind:"directive",type:H.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Ne.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:Ne.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:Se.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:Se.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:Se.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Ie.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,decorators:[{type:n,args:[{selector:"tb-arguments-map-config",providers:[{provide:B,useExisting:a((()=>Ln)),multi:!0},{provide:K,useExisting:a((()=>Ln)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class Tn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[...Wt.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(qe((e=>{let t;t="string"==typeof e&&Yt[e]?Yt[e]:null,this.updateView(t)})),Ae((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=Wt.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",Tn),Tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tn,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:B,useExisting:a((()=>Tn)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Pe.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,decorators:[{type:n,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:B,useExisting:a((()=>Tn)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:o,args:["operationInput",{static:!0}]}]}});class In extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Yt,this.ArgumentTypeResult=Xt,this.argumentTypeResultMap=an,this.attributeScopeMap=pn,this.argumentsResult=Object.values(Xt),this.attributeScopeResult=Object.values(mn)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[O.required]],arguments:[e?e.arguments:null,[O.required]],customFunction:[e?e.customFunction:"",[O.required]],result:this.fb.group({type:[e?e.result.type:null,[O.required]],attributeScope:[e?e.result.attributeScope:null,[O.required]],key:[e?e.result.key:"",[O.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Yt.CUSTOM?(this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}),null===this.mathFunctionConfigForm.get("customFunction").value&&this.mathFunctionConfigForm.get("customFunction").patchValue("(x - 32) / 1.8",{emitEvent:!1})):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===Xt.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",In),In.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),In.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:In,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ln,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:Tn,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,decorators:[{type:n,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Nn{constructor(){this.textAlign="left"}}e("ExampleHintComponent",Nn),Nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,deps:[],target:t.ɵɵFactoryTarget.Component}),Nn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Nn,selector:"tb-example-hint",inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink",textAlign:"textAlign"},ngImport:t,template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,decorators:[{type:n,args:[{selector:"tb-example-hint",template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"]}]}],propDecorators:{hintText:[{type:i}],popupHelpLink:[{type:i}],textAlign:[{type:i}]}});class Sn{constructor(e,t){this.injector=e,this.fb=t,this.propagateChange=()=>{},this.destroy$=new _e,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value&&e.controls.key.value&&e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof z||e instanceof U){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return ae(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(Be(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(){const e=this.kvListFormGroup.get("keyVals").value;if(!e.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const t of e)if(t.key===t.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",Sn),Sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,deps:[{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Sn,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:B,useExisting:a((()=>Sn)),multi:!0},{provide:K,useExisting:a((()=>Sn)),multi:!0}],ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],Sn.prototype,"disabled",void 0),Oe([T()],Sn.prototype,"uniqueKeyValuePairValidator",void 0),Oe([T()],Sn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,decorators:[{type:n,args:[{selector:"tb-kv-map-config",providers:[{provide:B,useExisting:a((()=>Sn)),multi:!0},{provide:K,useExisting:a((()=>Sn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class qn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(v),this.directionTypeTranslations=C,this.entityType=F,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[O.min(1)]],relationType:[null],deviceTypes:[null,[O.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",qn),qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:qn,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>qn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:je.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","floatLabel","label","disabled","entityType","emptyInputPlaceholder","filledInputPlaceholder","appearance","subscriptSizing","additionalClasses"]},{kind:"component",type:$e.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,decorators:[{type:n,args:[{selector:"tb-device-relations-query-config",providers:[{provide:B,useExisting:a((()=>qn)),multi:!0}],template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class An extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(v),this.directionTypeTranslations=C,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[O.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",An),An.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),An.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:An,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>An)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Qe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,decorators:[{type:n,args:[{selector:"tb-relations-query-config",providers:[{provide:B,useExisting:a((()=>An)),multi:!0}],template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Mn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[ge,ye,xe],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(I))this.messageTypesList.push({name:N.get(I[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Me(""),Ae((e=>e||"")),Ee((e=>this.fetchMessageTypes(e))),Ge())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ke(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ke(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",Mn),Mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,deps:[{token:P.Store},{token:Z.TranslateService},{token:S.TruncatePipe},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Mn,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:B,useExisting:a((()=>Mn)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:Ve.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:be.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:be.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:be.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:be.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Pe.HighlightPipe,name:"highlight"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,decorators:[{type:n,args:[{selector:"tb-message-types-config",providers:[{provide:B,useExisting:a((()=>Mn)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:S.TruncatePipe},{type:R.FormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:o,args:["chipList",{static:!1}]}],matAutocomplete:[{type:o,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:o,args:["messageTypeInput",{static:!1}]}]}});class En extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=zt,this.credentialsTypeTranslationsMap=Ut,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[O.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){ie(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([O.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[O.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(O.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",En),En.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),En.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:En,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:B,useExisting:a((()=>En)),multi:!0},{provide:K,useExisting:a((()=>En)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:H.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Je.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Je.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","maxSizeByte","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,decorators:[{type:n,args:[{selector:"tb-credentials-config",providers:[{provide:B,useExisting:a((()=>En)),multi:!0},{provide:K,useExisting:a((()=>En)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});const Gn=new l("WindowToken","undefined"!=typeof window&&window.document?{providedIn:"root",factory:()=>window}:{providedIn:"root",factory:()=>{}});class Dn{constructor(e,t,n){this.ngZone=e,this.document=t,this.window=n,this.copySubject=new _e,this.copyResponse$=this.copySubject.asObservable(),this.config={}}configure(e){this.config=e}copy(e){if(!this.isSupported||!e)return this.pushCopyResponse({isSuccess:!1,content:e});const t=this.copyFromContent(e);return t?this.pushCopyResponse({content:e,isSuccess:t}):this.pushCopyResponse({isSuccess:!1,content:e})}get isSupported(){return!!this.document.queryCommandSupported&&!!this.document.queryCommandSupported("copy")&&!!this.window}isTargetValid(e){if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement){if(e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');return!0}throw new Error("Target should be input or textarea")}copyFromInputElement(e,t=!0){try{this.selectTarget(e);const n=this.copyText();return this.clearSelection(t?e:void 0,this.window),n&&this.isCopySuccessInIE11()}catch(e){return!1}}isCopySuccessInIE11(){const e=this.window.clipboardData;return!(e&&e.getData&&!e.getData("Text"))}copyFromContent(e,t=this.document.body){if(this.tempTextArea&&!t.contains(this.tempTextArea)&&this.destroy(this.tempTextArea.parentElement||void 0),!this.tempTextArea){this.tempTextArea=this.createTempTextArea(this.document,this.window);try{t.appendChild(this.tempTextArea)}catch(e){throw new Error("Container should be a Dom element")}}this.tempTextArea.value=e;const n=this.copyFromInputElement(this.tempTextArea,!1);return this.config.cleanUpAfterCopy&&this.destroy(this.tempTextArea.parentElement||void 0),n}destroy(e=this.document.body){this.tempTextArea&&(e.removeChild(this.tempTextArea),this.tempTextArea=void 0)}selectTarget(e){return e.select(),e.setSelectionRange(0,e.value.length),e.value.length}copyText(){return this.document.execCommand("copy")}clearSelection(e,t){e&&e.focus(),t.getSelection()?.removeAllRanges()}createTempTextArea(e,t){const n="rtl"===e.documentElement.getAttribute("dir");let r;r=e.createElement("textarea"),r.style.fontSize="12pt",r.style.border="0",r.style.padding="0",r.style.margin="0",r.style.position="absolute",r.style[n?"right":"left"]="-9999px";const o=t.pageYOffset||e.documentElement.scrollTop;return r.style.top=o+"px",r.setAttribute("readonly",""),r}pushCopyResponse(e){this.copySubject.observers.length>0&&this.ngZone.run((()=>{this.copySubject.next(e)}))}pushCopyReponse(e){this.pushCopyResponse(e)}}Dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,deps:[{token:t.NgZone},{token:j},{token:Gn,optional:!0}],target:t.ɵɵFactoryTarget.Injectable}),Dn.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Dn,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:void 0,decorators:[{type:m,args:[j]}]},{type:void 0,decorators:[{type:p},{type:m,args:[Gn]}]}]}});class wn{constructor(e,t,n,o){this.ngZone=e,this.host=t,this.renderer=n,this.clipboardSrv=o,this.cbOnSuccess=new r,this.cbOnError=new r,this.onClick=e=>{this.clipboardSrv.isSupported?this.targetElm&&this.clipboardSrv.isTargetValid(this.targetElm)?this.handleResult(this.clipboardSrv.copyFromInputElement(this.targetElm),this.targetElm.value,e):this.cbContent&&this.handleResult(this.clipboardSrv.copyFromContent(this.cbContent,this.container),this.cbContent,e):this.handleResult(!1,void 0,e)}}ngOnInit(){this.ngZone.runOutsideAngular((()=>{this.clickListener=this.renderer.listen(this.host.nativeElement,"click",this.onClick)}))}ngOnDestroy(){this.clickListener&&this.clickListener(),this.clipboardSrv.destroy(this.container)}handleResult(e,t,n){let r={isSuccess:e,content:t,successMessage:this.cbSuccessMsg,event:n};e?this.cbOnSuccess.observed&&this.ngZone.run((()=>{this.cbOnSuccess.emit(r)})):this.cbOnError.observed&&this.ngZone.run((()=>{this.cbOnError.emit(r)})),this.clipboardSrv.pushCopyResponse(r)}}wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:wn,deps:[{token:t.NgZone},{token:t.ElementRef},{token:t.Renderer2},{token:Dn}],target:t.ɵɵFactoryTarget.Directive}),wn.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:wn,selector:"[ngxClipboard]",inputs:{targetElm:["ngxClipboard","targetElm"],container:"container",cbContent:"cbContent",cbSuccessMsg:"cbSuccessMsg"},outputs:{cbOnSuccess:"cbOnSuccess",cbOnError:"cbOnError"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:wn,decorators:[{type:d,args:[{selector:"[ngxClipboard]"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:t.ElementRef},{type:t.Renderer2},{type:Dn}]},propDecorators:{targetElm:[{type:i,args:["ngxClipboard"]}],container:[{type:i}],cbContent:[{type:i}],cbSuccessMsg:[{type:i}],cbOnSuccess:[{type:u}],cbOnError:[{type:u}]}});class Vn{constructor(e,t,n){this._clipboardService=e,this._viewContainerRef=t,this._templateRef=n}ngOnInit(){this._clipboardService.isSupported&&this._viewContainerRef.createEmbeddedView(this._templateRef)}}Vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Vn,deps:[{token:Dn},{token:t.ViewContainerRef},{token:t.TemplateRef}],target:t.ɵɵFactoryTarget.Directive}),Vn.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:Vn,selector:"[ngxClipboardIfSupported]",ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Vn,decorators:[{type:d,args:[{selector:"[ngxClipboardIfSupported]"}]}],ctorParameters:function(){return[{type:Dn},{type:t.ViewContainerRef},{type:t.TemplateRef}]}});class Pn{}Pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Pn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,declarations:[wn,Vn],imports:[$],exports:[wn,Vn]}),Pn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,imports:[[$]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,decorators:[{type:c,args:[{imports:[$],declarations:[wn,Vn],exports:[wn,Vn]}]}]});class Rn{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e){this.fb=e,this.subscriptSizing="fixed",this.messageTypes=[{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},{name:"Custom",value:""}],this.propagateChange=()=>{},this.destroy$=new _e,this.messageTypeFormGroup=this.fb.group({messageTypeAlias:[null,[O.required]],messageType:[{value:null,disabled:!0},[O.maxLength(255)]]}),this.messageTypeFormGroup.get("messageTypeAlias").valueChanges.pipe(Be(this.destroy$)).subscribe((e=>this.updateMessageTypeValue(e))),this.messageTypeFormGroup.valueChanges.pipe(Be(this.destroy$)).subscribe((()=>this.updateView()))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}writeValue(e){this.modelValue=e;let t=this.messageTypes.find((t=>t.value===e));t||(t=this.messageTypes.find((e=>""===e.value))),this.messageTypeFormGroup.get("messageTypeAlias").patchValue(t,{emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1})}validate(){return this.messageTypeFormGroup.valid?null:{messageTypeInvalid:!0}}setDisabledState(e){this.disabled=e,e?this.messageTypeFormGroup.disable({emitEvent:!1}):(this.messageTypeFormGroup.enable({emitEvent:!1}),"Custom"!==this.messageTypeFormGroup.get("messageTypeAlias").value?.name&&this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}))}updateView(){const e=this.messageTypeFormGroup.getRawValue().messageType;this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[O.required,O.maxLength(255)]:[O.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}updateMessageTypeValue(e){"Custom"!==e?.name?this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}):this.messageTypeFormGroup.get("messageType").enable({emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e.value??null)}}e("OutputMessageTypeAutocompleteComponent",Rn),Rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,deps:[{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Rn,selector:"tb-output-message-type-autocomplete",inputs:{subscriptSizing:"subscriptSizing",disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>Rn)),multi:!0},{provide:K,useExisting:a((()=>Rn)),multi:!0}],ngImport:t,template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:wn,selector:"[ngxClipboard]",inputs:["ngxClipboard","container","cbContent","cbSuccessMsg"],outputs:["cbOnSuccess","cbOnError"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],Rn.prototype,"disabled",void 0),Oe([T()],Rn.prototype,"required",null),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rn,decorators:[{type:n,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:B,useExisting:a((()=>Rn)),multi:!0},{provide:K,useExisting:a((()=>Rn)),multi:!0}],template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n'}]}],ctorParameters:function(){return[{type:R.FormBuilder}]},propDecorators:{subscriptSizing:[{type:i}],disabled:[{type:i}],required:[{type:i}]}});class On{constructor(e,t){this.fb=e,this.translate=t,this.translation=nn,this.propagateChange=()=>{},this.destroy$=new _e,this.selectOptions=[]}ngOnInit(){this.initOptions(),this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(De(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}initOptions(){for(const e of this.translation.keys())this.selectOptions.push({value:e,name:this.translate.instant(this.translation.get(e))})}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}}e("MsgMetadataChipComponent",On),On.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,deps:[{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),On.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:On,selector:"tb-msg-metadata-chip",inputs:{labelText:"labelText",translation:"translation"},providers:[{provide:B,useExisting:a((()=>On)),multi:!0}],ngImport:t,template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:be.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:be.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:On,decorators:[{type:n,args:[{selector:"tb-msg-metadata-chip",providers:[{provide:B,useExisting:a((()=>On)),multi:!0}],template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n'}]}],ctorParameters:function(){return[{type:R.FormBuilder},{type:Z.TranslateService}]},propDecorators:{labelText:[{type:i}],translation:[{type:i}]}});class _n extends L{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new _e,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof z||e instanceof U){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return ae(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(_),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(De(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[O.required]],value:[t.value,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)ie(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[O.required]],value:["",[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(De(this.destroy$)).subscribe((t=>{const n=Lt.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("SvMapConfigComponent",_n),_n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,deps:[{token:P.Store},{token:Z.TranslateService},{token:t.Injector},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),_n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_n,selector:"tb-sv-map-config",inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:B,useExisting:a((()=>_n)),multi:!0},{provide:K,useExisting:a((()=>_n)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Ie.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:H.AsyncPipe,name:"async"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),Oe([T()],_n.prototype,"disabled",void 0),Oe([T()],_n.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_n,decorators:[{type:n,args:[{selector:"tb-sv-map-config",providers:[{provide:B,useExisting:a((()=>_n)),multi:!0},{provide:K,useExisting:a((()=>_n)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:t.Injector},{type:R.FormBuilder}]},propDecorators:{selectOptions:[{type:i}],disabled:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],targetKeyPrefix:[{type:i}],selectText:[{type:i}],selectRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class Bn extends L{get required(){return this.requiredValue}set required(e){this.requiredValue=Fe(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(v),this.directionTypeTranslations=C,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[O.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigOldComponent",Bn),Bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Bn,selector:"tb-relations-query-config-old",inputs:{disabled:"disabled",required:"required"},providers:[{provide:B,useExisting:a((()=>Bn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Qe.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,decorators:[{type:n,args:[{selector:"tb-relations-query-config-old",providers:[{provide:B,useExisting:a((()=>Bn)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Kn{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new _e,this.separatorKeysCodes=[ge,ye,xe],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[[],[]],sharedAttributeNames:[[],[]],serverAttributeNames:[[],[]],latestTsKeyNames:[[],[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(O.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(De(this.destroy$)).subscribe((e=>{this.propagateChange(this.preparePropagateValue(e))}))}preparePropagateValue(e){const t={};for(const n in e)t[n]="getLatestValueWithTs"===n||ie(e[n])?e[n]:[];return t}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}}e("SelectAttributesComponent",Kn),Kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,deps:[{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Kn,selector:"tb-select-attributes",inputs:{popupHelpLink:"popupHelpLink"},providers:[{provide:B,useExisting:a((()=>Kn)),multi:!0},{provide:K,useExisting:Kn,multi:!0}],ngImport:t,template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,decorators:[{type:n,args:[{selector:"tb-select-attributes",providers:[{provide:B,useExisting:a((()=>Kn)),multi:!0},{provide:K,useExisting:Kn,multi:!0}],template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:Z.TranslateService},{type:R.FormBuilder}]},propDecorators:{popupHelpLink:[{type:i}]}});class zn extends L{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new _e,this.alarmStatus=q,this.alarmStatusTranslations=A}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(De(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}}e("AlarmStatusSelectComponent",zn),zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:zn,selector:"tb-alarm-status-select",providers:[{provide:B,useExisting:a((()=>zn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"],dependencies:[{kind:"component",type:be.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:be.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,decorators:[{type:n,args:[{selector:"tb-alarm-status-select",providers:[{provide:B,useExisting:a((()=>zn)),multi:!0}],template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Un{}e("RulenodeCoreConfigCommonModule",Un),Un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Un.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Un,declarations:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn],imports:[$,M,Re],exports:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn]}),Un.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,imports:[$,M,Re]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,decorators:[{type:c,args:[{declarations:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn],imports:[$,M,Re],exports:[Sn,qn,An,Mn,En,Ln,Tn,Rn,hn,On,_n,Bn,Kn,zn,Nn]}]}]});class Hn{}e("RuleNodeCoreConfigActionModule",Hn),Hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Hn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Hn,declarations:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In],imports:[$,M,Re,Un],exports:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In]}),Hn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,imports:[$,M,Re,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,decorators:[{type:c,args:[{declarations:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In],imports:[$,M,Re,Un],exports:[kn,ct,Cn,bn,un,ut,ft,gt,yt,fn,xt,ht,dn,cn,xn,vn,Fn,bt,yn,gn,In]}]}]});class jn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ge,ye,xe]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[O.min(0),O.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]]})}prepareInputConfig(e){return{inputValueKey:ie(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:ie(e?.outputValueKey)?e.outputValueKey:null,useCache:!ie(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!ie(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:ie(e?.periodValueKey)?e.periodValueKey:null,round:ie(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!ie(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative}}prepareOutputConfig(e){return le(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([O.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",jn),jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:jn,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n",dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n"}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class $n extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Pt;for(const e of Rt.keys())e!==Pt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,le(e)}prepareInputConfig(e){let t,n;return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[O.required]],fetchTo:[e.fetchTo]})}}e("CustomerAttributesConfigComponent",$n),$n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),$n.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:$n,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,decorators:[{type:n,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Qn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[O.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return se(e)&&(e.attributesControl={clientAttributeNames:ie(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:ie(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:ie(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!ie(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:ie(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!ie(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("DeviceAttributesConfigComponent",Qn),Qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Qn,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:qn,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Kn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Jn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(Mt))this.predefinedValues.push({value:Mt[e],name:this.translate.instant(Et.get(Mt[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=ie(e?.addToMetadata)?e.addToMetadata?en.METADATA:en.DATA:e?.fetchTo?e.fetchTo:en.DATA,{detailsList:ie(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[O.required]],fetchTo:[e.fetchTo,[]]})}}e("EntityDetailsConfigComponent",Jn),Jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Jn,selector:"tb-enrichment-node-entity-details-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Yn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[ge,ye,xe],this.aggregationTypes=E,this.aggregations=Object.values(E),this.aggregationTypesTranslations=G,this.fetchMode=Gt,this.samplingOrders=Object.values(Vt),this.samplingOrdersTranslate=_t,this.timeUnits=Object.values(Nt),this.timeUnitsTranslationMap=St,this.deduplicationStrategiesHintTranslations=wt,this.headerOptions=[],this.timeUnitMap={[Nt.MILLISECONDS]:1,[Nt.SECONDS]:1e3,[Nt.MINUTES]:6e4,[Nt.HOURS]:36e5,[Nt.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Dt.keys())this.headerOptions.push({value:e,name:this.translate.instant(Dt.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[O.required]],aggregation:[e.aggregation,[O.required]],fetchMode:[e.fetchMode,[O.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,le(e)}prepareInputConfig(e){return se(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:ie(e?.aggregation)?e.aggregation:E.NONE,fetchMode:ie(e?.fetchMode)?e.fetchMode:Gt.FIRST,orderBy:ie(e?.orderBy)?e.orderBy:Vt.ASC,limit:ie(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!ie(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:ie(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:ie(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:Nt.MINUTES,endInterval:ie(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:ie(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:Nt.MINUTES},startIntervalPattern:ie(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:ie(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===Gt.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([O.required,O.min(2),O.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([O.required,O.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([O.required,O.min(1),O.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([O.required,O.min(1),O.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([O.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===Gt.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===E.NONE}}e("GetTelemetryFromDatabaseConfigComponent",Yn),Yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Yn,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Wn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return se(e)&&(e.attributesControl={clientAttributeNames:ie(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:ie(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:ie(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:ie(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!ie(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA,tellFailureIfAbsent:!!ie(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:ie(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("OriginatorAttributesConfigComponent",Wn),Wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,deps:[{token:P.Store},{token:Z.TranslateService},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Wn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Wn,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Kn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:Z.TranslateService},{type:R.FormBuilder}]}});class Zn extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of kt)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return le(e)}prepareInputConfig(e){return{dataMapping:ie(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:ie(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[O.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}}e("OriginatorFieldsConfigComponent",Zn),Zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Zn,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n',dependencies:[{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:_n,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Xn extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=Pt,this.msgMetadataLabelTranslations=Ot,this.originatorFields=[],this.fetchToData=[];for(const e of Object.keys(kt))this.originatorFields.push({value:kt[e].value,name:this.translate.instant(kt[e].name)});for(const e of Rt.keys())this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===Pt.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,le(e)}prepareInputConfig(e){let t,n,r={[k.name.value]:`relatedEntity${this.translate.instant(k.name.name)}`},o={serialNumber:"sn"};return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,t===Pt.FIELDS?r=n:o=n,{relationsQuery:ie(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:o,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[O.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[O.required]],svMap:[e.svMap,[O.required]],fetchTo:[e.fetchTo,[]]})}validatorTriggers(){return["dataToFetch"]}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===Pt.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}}e("RelatedAttributesConfigComponent",Xn),Xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Xn,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:An,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:_n,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class er extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=Pt;for(const e of Rt.keys())e!==Pt.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Rt.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=ie(e?.telemetry)?e.telemetry?Pt.LATEST_TELEMETRY:Pt.ATTRIBUTES:ie(e?.dataToFetch)?e.dataToFetch:Pt.ATTRIBUTES,n=ie(e?.attrMapping)?e.attrMapping:ie(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===Pt.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[O.required]],fetchTo:[e.fetchTo,[]]})}}e("TenantAttributesConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:er,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:W.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,decorators:[{type:n,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class tr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:ie(e?.fetchTo)?e.fetchTo:en.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}}e("FetchDeviceCredentialsConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tr,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,decorators:[{type:n,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class nr{}e("RulenodeCoreConfigEnrichmentModule",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),nr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:nr,declarations:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr],imports:[$,M,Un],exports:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr]}),nr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,decorators:[{type:c,args:[{declarations:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr],imports:[$,M,Un],exports:[$n,Jn,Qn,Wn,Zn,Yn,Xn,er,jn,tr]}]}]});class rr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Ht,this.azureIotHubCredentialsTypeTranslationsMap=jt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[O.required,O.min(1),O.max(200)]],clientId:[e?e.clientId:null,[O.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[O.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([O.required]);break;case"cert.PEM":t.get("privateKey").setValidators([O.required]),t.get("privateKeyFileName").setValidators([O.required]),t.get("cert").setValidators([O.required]),t.get("certFileName").setValidators([O.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rr,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:H.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Je.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Je.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:R.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","maxSizeByte","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,decorators:[{type:n,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class or extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Qt,this.ToByteStandartCharsetTypeTranslationMap=Jt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[O.required]],retries:[e?e.retries:null,[O.min(0)]],batchSize:[e?e.batchSize:null,[O.min(0)]],linger:[e?e.linger:null,[O.min(0)]],bufferMemory:[e?e.bufferMemory:null,[O.min(0)]],acks:[e?e.acks:null,[O.required]],keySerializer:[e?e.keySerializer:null,[O.required]],valueSerializer:[e?e.valueSerializer:null,[O.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([O.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:or,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,decorators:[{type:n,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ar extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[O.required]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[O.required,O.min(1),O.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&me(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){me(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}}e("MqttConfigComponent",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ar,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:En,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,decorators:[{type:n,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ir extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=D,this.entityType=F}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[O.required]],targets:[e?e.targets:[],[O.required]]})}}e("NotificationConfigComponent",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ir,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:tt.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:nt.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","allowEdit","disabled","notificationTypes"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,decorators:[{type:n,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class lr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[O.required]],topicName:[e?e.topicName:null,[O.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[O.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[O.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:lr,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ye.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","maxSizeByte","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,decorators:[{type:n,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class sr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[O.required]],port:[e?e.port:null,[O.required,O.min(1),O.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[O.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[O.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:sr,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,decorators:[{type:n,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class mr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys($t)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[O.required]],requestMethod:[e?e.requestMethod:null,[O.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[O.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,o=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!o?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[O.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[O.required,O.min(1),O.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([O.min(0)])),n?this.restApiCallConfigForm.get("maxQueueSize").setValidators([O.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:mr,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:En,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,decorators:[{type:n,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class pr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([O.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([O.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([O.required,O.min(1),O.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([O.required,O.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[O.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[O.required,O.min(1),O.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:pr,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:rt.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:We.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,decorators:[{type:n,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class dr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[O.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[O.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([O.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dr,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ot.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,decorators:[{type:n,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class ur extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(w),this.slackChanelTypesTranslateMap=V}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[O.required]],conversationType:[e?e.conversationType:null,[O.required]],conversation:[e?e.conversation:null,[O.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([O.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),ur.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ur,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Q.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:at.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:at.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:it.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,decorators:[{type:n,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[O.required]],accessKeyId:[e?e.accessKeyId:null,[O.required]],secretAccessKey:[e?e.secretAccessKey:null,[O.required]],region:[e?e.region:null,[O.required]]})}}e("SnsConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cr,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,decorators:[{type:n,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class fr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Bt,this.sqsQueueTypes=Object.keys(Bt),this.sqsQueueTypeTranslationsMap=Kt}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[O.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[O.required]],delaySeconds:[e?e.delaySeconds:null,[O.min(0),O.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[O.required]],secretAccessKey:[e?e.secretAccessKey:null,[O.required]],region:[e?e.region:null,[O.required]]})}}e("SqsConfigComponent",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fr,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:hn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:he.SafePipe,name:"safe"},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,decorators:[{type:n,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class gr{}e("RulenodeCoreConfigExternalModule",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),gr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:gr,declarations:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur],imports:[$,M,Re,Un],exports:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur]}),gr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,imports:[$,M,Re,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,decorators:[{type:c,args:[{declarations:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur],imports:[$,M,Re,Un],exports:[cr,fr,lr,or,ar,ir,sr,mr,pr,rr,dr,ur]}]}]});class yr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:ie(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[O.required]]})}}e("CheckAlarmStatusComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yr,selector:"tb-filter-node-check-alarm-status-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:zn,selector:"tb-alarm-status-select"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class xr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:ie(e?.messageNames)?e.messageNames:[],metadataNames:ie(e?.metadataNames)?e.metadataNames:[],checkAllKeys:!!ie(e?.checkAllKeys)&&e.checkAllKeys}}prepareOutputConfig(e){return{messageNames:ie(e?.messageNames)?e.messageNames:[],metadataNames:ie(e?.metadataNames)?e.metadataNames:[],checkAllKeys:e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(O.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}}e("CheckMessageConfigComponent",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),xr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xr,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-message-config",template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class br extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(v),this.entitySearchDirectionTranslationsMap=C}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!ie(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:ie(e?.direction)?e.direction:null,entityType:ie(e?.entityType)?e.entityType:null,entityId:ie(e?.entityId)?e.entityId:null,relationType:ie(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[O.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[O.required]:[]],relationType:[e.relationType,[O.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[O.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[O.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),br.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:br,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:lt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:$e.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,decorators:[{type:n,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class hr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=Tt,this.perimeterTypes=Object.values(Tt),this.perimeterTypeTranslationMap=It,this.rangeUnits=Object.values(qt),this.rangeUnitTranslationMap=At,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:ie(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:ie(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:ie(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!ie(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:ie(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:ie(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:ie(e?.centerLongitude)?e.centerLongitude:null,range:ie(e?.range)?e.range:null,rangeUnit:ie(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:ie(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[O.required]],longitudeKeyName:[e.longitudeKeyName,[O.required]],perimeterType:[e.perimeterType,[O.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([O.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==Tt.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([O.required,O.min(-90),O.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([O.required,O.min(-180),O.max(180)]),this.geoFilterConfigForm.get("range").setValidators([O.required,O.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([O.required]),this.defaultPaddingEnable=!1),t||n!==Tt.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([O.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",hr),hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:te.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:W.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:R.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ne.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,decorators:[{type:n,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class vr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:ie(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[O.required]]})}}e("MessageTypeConfigComponent",vr),vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Mn,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,decorators:[{type:n,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[F.DEVICE,F.ASSET,F.ENTITY_VIEW,F.TENANT,F.CUSTOMER,F.USER,F.DASHBOARD,F.RULE_CHAIN,F.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:ie(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[O.required]]})}}e("OriginatorTypeConfigComponent",Cr),Cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:st.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","additionalClasses","appearance","label","floatLabel","disabled","subscriptSizing","allowedEntityTypes","emptyInputPlaceholder","filledInputPlaceholder","ignoreAuthorityFilter"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:W.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,decorators:[{type:n,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Fr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[O.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),{scriptLang:ie(e?.scriptLang)?e.scriptLang:x.JS,jsScript:ie(e?.jsScript)?e.jsScript:null,tbelScript:ie(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Fr),Fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fr,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,decorators:[{type:n,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class kr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[O.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),{scriptLang:ie(e?.scriptLang)?e.scriptLang:x.JS,jsScript:ie(e?.jsScript)?e.jsScript:null,tbelScript:ie(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",o=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",kr),kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,deps:[{token:P.Store},{token:R.UntypedFormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kr,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,decorators:[{type:n,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Lr{}e("RuleNodeCoreConfigFilterModule",Lr),Lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Lr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Lr,declarations:[xr,br,hr,vr,Cr,Fr,kr,yr],imports:[$,M,Un],exports:[xr,br,hr,vr,Cr,Fr,kr,yr]}),Lr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,decorators:[{type:c,args:[{declarations:[xr,br,hr,vr,Cr,Fr,kr,yr],imports:[$,M,Un],exports:[xr,br,hr,vr,Cr,Fr,kr,yr]}]}]});class Tr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=vt,this.originatorSources=Object.keys(vt),this.originatorSourceTranslationMap=Ct,this.originatorSourceDescTranslationMap=Ft,this.allowedEntityTypes=[F.DEVICE,F.ASSET,F.ENTITY_VIEW,F.USER,F.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[O.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===vt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([O.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===vt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([O.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([O.required,O.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Tr),Tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tr,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ve.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ne.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:Ne.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:An,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,decorators:[{type:n,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Ir extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=re(this.store).tbelEnabled,this.scriptLanguage=x,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:x.JS,[O.required]],jsScript:[e?e.jsScript:null,[O.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==x.TBEL||this.tbelEnabled||(t=x.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===x.JS?[O.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===x.TBEL?[O.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=x.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===x.JS?"jsScript":"tbelScript",r=t===x.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===x.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",Ir),Ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,deps:[{token:P.Store},{token:R.FormBuilder},{token:oe.NodeScriptTestService},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ir,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:de.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:de.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:fe.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,decorators:[{type:n,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:oe.NodeScriptTestService},{type:Z.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + const Nr=mt({passive:!0});class Sr{constructor(e,t){this._platform=e,this._ngZone=t,this._monitoredElements=new Map}monitor(e){if(!this._platform.isBrowser)return ze;const t=ke(e),n=this._monitoredElements.get(t);if(n)return n.subject;const r=new _e,o="cdk-text-field-autofilled",a=e=>{"cdk-text-field-autofill-start"!==e.animationName||t.classList.contains(o)?"cdk-text-field-autofill-end"===e.animationName&&t.classList.contains(o)&&(t.classList.remove(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!1})))):(t.classList.add(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!0}))))};return this._ngZone.runOutsideAngular((()=>{t.addEventListener("animationstart",a,Nr),t.classList.add("cdk-text-field-autofill-monitored")})),this._monitoredElements.set(t,{subject:r,unlisten:()=>{t.removeEventListener("animationstart",a,Nr)}}),r}stopMonitoring(e){const t=ke(e),n=this._monitoredElements.get(t);n&&(n.unlisten(),n.subject.complete(),t.classList.remove("cdk-text-field-autofill-monitored"),t.classList.remove("cdk-text-field-autofilled"),this._monitoredElements.delete(t))}ngOnDestroy(){this._monitoredElements.forEach(((e,t)=>this.stopMonitoring(t)))}}Sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,deps:[{token:pt.Platform},{token:t.NgZone}],target:t.ɵɵFactoryTarget.Injectable}),Sr.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Sr,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:pt.Platform},{type:t.NgZone}]}});class qr{constructor(e,t){this._elementRef=e,this._autofillMonitor=t,this.cdkAutofill=new r}ngOnInit(){this._autofillMonitor.monitor(this._elementRef).subscribe((e=>this.cdkAutofill.emit(e)))}ngOnDestroy(){this._autofillMonitor.stopMonitoring(this._elementRef)}}qr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:qr,deps:[{token:t.ElementRef},{token:Sr}],target:t.ɵɵFactoryTarget.Directive}),qr.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:qr,selector:"[cdkAutofill]",outputs:{cdkAutofill:"cdkAutofill"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:qr,decorators:[{type:d,args:[{selector:"[cdkAutofill]"}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:Sr}]},propDecorators:{cdkAutofill:[{type:u}]}}); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + class Ar{get minRows(){return this._minRows}set minRows(e){this._minRows=Le(e),this._setMinHeight()}get maxRows(){return this._maxRows}set maxRows(e){this._maxRows=Le(e),this._setMaxHeight()}get enabled(){return this._enabled}set enabled(e){e=Fe(e),this._enabled!==e&&((this._enabled=e)?this.resizeToFitContent(!0):this.reset())}get placeholder(){return this._textareaElement.placeholder}set placeholder(e){this._cachedPlaceholderHeight=void 0,e?this._textareaElement.setAttribute("placeholder",e):this._textareaElement.removeAttribute("placeholder"),this._cacheTextareaPlaceholderHeight()}constructor(e,t,n,r){this._elementRef=e,this._platform=t,this._ngZone=n,this._destroyed=new _e,this._enabled=!0,this._previousMinRows=-1,this._isViewInited=!1,this._handleFocusEvent=e=>{this._hasFocus="focus"===e.type},this._document=r,this._textareaElement=this._elementRef.nativeElement}_setMinHeight(){const e=this.minRows&&this._cachedLineHeight?this.minRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.minHeight=e)}_setMaxHeight(){const e=this.maxRows&&this._cachedLineHeight?this.maxRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.maxHeight=e)}ngAfterViewInit(){this._platform.isBrowser&&(this._initialHeight=this._textareaElement.style.height,this.resizeToFitContent(),this._ngZone.runOutsideAngular((()=>{const e=this._getWindow();Ue(e,"resize").pipe(we(16),De(this._destroyed)).subscribe((()=>this.resizeToFitContent(!0))),this._textareaElement.addEventListener("focus",this._handleFocusEvent),this._textareaElement.addEventListener("blur",this._handleFocusEvent)})),this._isViewInited=!0,this.resizeToFitContent(!0))}ngOnDestroy(){this._textareaElement.removeEventListener("focus",this._handleFocusEvent),this._textareaElement.removeEventListener("blur",this._handleFocusEvent),this._destroyed.next(),this._destroyed.complete()}_cacheTextareaLineHeight(){if(this._cachedLineHeight)return;let e=this._textareaElement.cloneNode(!1);e.rows=1,e.style.position="absolute",e.style.visibility="hidden",e.style.border="none",e.style.padding="0",e.style.height="",e.style.minHeight="",e.style.maxHeight="",e.style.overflow="hidden",this._textareaElement.parentNode.appendChild(e),this._cachedLineHeight=e.clientHeight,e.remove(),this._setMinHeight(),this._setMaxHeight()}_measureScrollHeight(){const e=this._textareaElement,t=e.style.marginBottom||"",n=this._platform.FIREFOX,r=n&&this._hasFocus,o=n?"cdk-textarea-autosize-measuring-firefox":"cdk-textarea-autosize-measuring";r&&(e.style.marginBottom=`${e.clientHeight}px`),e.classList.add(o);const a=e.scrollHeight-4;return e.classList.remove(o),r&&(e.style.marginBottom=t),a}_cacheTextareaPlaceholderHeight(){if(!this._isViewInited||null!=this._cachedPlaceholderHeight)return;if(!this.placeholder)return void(this._cachedPlaceholderHeight=0);const e=this._textareaElement.value;this._textareaElement.value=this._textareaElement.placeholder,this._cachedPlaceholderHeight=this._measureScrollHeight(),this._textareaElement.value=e}ngDoCheck(){this._platform.isBrowser&&this.resizeToFitContent()}resizeToFitContent(e=!1){if(!this._enabled)return;if(this._cacheTextareaLineHeight(),this._cacheTextareaPlaceholderHeight(),!this._cachedLineHeight)return;const t=this._elementRef.nativeElement,n=t.value;if(!e&&this._minRows===this._previousMinRows&&n===this._previousValue)return;const r=this._measureScrollHeight(),o=Math.max(r,this._cachedPlaceholderHeight||0);t.style.height=`${o}px`,this._ngZone.runOutsideAngular((()=>{"undefined"!=typeof requestAnimationFrame?requestAnimationFrame((()=>this._scrollToCaretPosition(t))):setTimeout((()=>this._scrollToCaretPosition(t)))})),this._previousValue=n,this._previousMinRows=this._minRows}reset(){void 0!==this._initialHeight&&(this._textareaElement.style.height=this._initialHeight)}_noopInputHandler(){}_getDocument(){return this._document||document}_getWindow(){return this._getDocument().defaultView||window}_scrollToCaretPosition(e){const{selectionStart:t,selectionEnd:n}=e;!this._destroyed.isStopped&&this._hasFocus&&e.setSelectionRange(t,n)}}Ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Ar,deps:[{token:t.ElementRef},{token:pt.Platform},{token:t.NgZone},{token:j,optional:!0}],target:t.ɵɵFactoryTarget.Directive}),Ar.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:Ar,selector:"textarea[cdkTextareaAutosize]",inputs:{minRows:["cdkAutosizeMinRows","minRows"],maxRows:["cdkAutosizeMaxRows","maxRows"],enabled:["cdkTextareaAutosize","enabled"],placeholder:"placeholder"},host:{attributes:{rows:"1"},listeners:{input:"_noopInputHandler()"},classAttribute:"cdk-textarea-autosize"},exportAs:["cdkTextareaAutosize"],ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Ar,decorators:[{type:d,args:[{selector:"textarea[cdkTextareaAutosize]",exportAs:"cdkTextareaAutosize",host:{class:"cdk-textarea-autosize",rows:"1","(input)":"_noopInputHandler()"}}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:pt.Platform},{type:t.NgZone},{type:void 0,decorators:[{type:p},{type:m,args:[j]}]}]},propDecorators:{minRows:[{type:i,args:["cdkAutosizeMinRows"]}],maxRows:[{type:i,args:["cdkAutosizeMaxRows"]}],enabled:[{type:i,args:["cdkTextareaAutosize"]}],placeholder:[{type:i}]}}); + /** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + class Mr{}Mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Mr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,declarations:[qr,Ar],exports:[qr,Ar]}),Mr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,decorators:[{type:c,args:[{declarations:[qr,Ar],exports:[qr,Ar]}]}]});class Er extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",description:"tb.mail-body-type.plain-text-description",value:"false"},{name:"tb.mail-body-type.html",description:"tb.mail-body-type.html-text-description",value:"true"},{name:"tb.mail-body-type.use-body-type-template",description:"tb.mail-body-type.dynamic-text-description",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[O.required]],toTemplate:[e?e.toTemplate:null,[O.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[O.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[O.required]],bodyTemplate:[e?e.bodyTemplate:null,[O.required]]})}prepareInputConfig(e){return{fromTemplate:ie(e?.fromTemplate)?e.fromTemplate:null,toTemplate:ie(e?.toTemplate)?e.toTemplate:null,ccTemplate:ie(e?.ccTemplate)?e.ccTemplate:null,bccTemplate:ie(e?.bccTemplate)?e.bccTemplate:null,subjectTemplate:ie(e?.subjectTemplate)?e.subjectTemplate:null,mailBodyType:ie(e?.mailBodyType)?e.mailBodyType:null,isHtmlTemplate:ie(e?.isHtmlTemplate)?e.isHtmlTemplate:null,bodyTemplate:ie(e?.bodyTemplate)?e.bodyTemplate:null}}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}getBodyTypeName(){return this.mailBodyTypes.find((e=>e.value===this.toEmailConfigForm.get("mailBodyType").value)).name}}e("ToEmailConfigComponent",Er),Er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Er,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Er,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Ar,selector:"textarea[cdkTextareaAutosize]",inputs:["cdkAutosizeMinRows","cdkAutosizeMaxRows","cdkTextareaAutosize","placeholder"],exportAs:["cdkTextareaAutosize"]},{kind:"component",type:X.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:X.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:ee.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Ne.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:Ne.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Er,decorators:[{type:n,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Gr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.copyFrom=[],this.translation=tn;for(const e of this.translation.keys())this.copyFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({copyFrom:[e.copyFrom,[O.required]],keys:[e?e.keys:null,[O.required]]})}configForm(){return this.copyKeysConfigForm}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.copyFrom?en.METADATA:en.DATA:ie(e?.copyFrom)?e.copyFrom:en.DATA,{keys:ie(e?.keys)?e.keys:null,copyFrom:t}}}e("CopyKeysConfigComponent",Gr),Gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Gr,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gr,decorators:[{type:n,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Dr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.renameIn=[],this.translation=rn;for(const e of this.translation.keys())this.renameIn.push({value:e,name:this.translate.instant(this.translation.get(e))})}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({renameIn:[e?e.renameIn:null,[O.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[O.required]]})}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.fromMetadata?en.METADATA:en.DATA:ie(e?.renameIn)?e?.renameIn:en.DATA,{renameKeysMapping:ie(e?.renameKeysMapping)?e.renameKeysMapping:null,renameIn:t}}}e("RenameKeysConfigComponent",Dr),Dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Dr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"],dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Sn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dr,decorators:[{type:n,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"]}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class wr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[O.required]]})}}e("NodeJsonPathConfigComponent",wr),wr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),wr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:wr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n",dependencies:[{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,decorators:[{type:n,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n"}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Vr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.deleteFrom=[],this.translation=nn;for(const e of this.translation.keys())this.deleteFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({deleteFrom:[e.deleteFrom,[O.required]],keys:[e?e.keys:null,[O.required]]})}prepareInputConfig(e){let t;return t=ie(e?.fromMetadata)?e.fromMetadata?en.METADATA:en.DATA:ie(e?.deleteFrom)?e?.deleteFrom:en.DATA,{keys:ie(e?.keys)?e.keys:null,deleteFrom:t}}configForm(){return this.deleteKeysConfigForm}}e("DeleteKeysConfigComponent",Vr),Vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,deps:[{token:P.Store},{token:R.FormBuilder},{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Vr,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:Ze.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:On,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,decorators:[{type:n,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder},{type:Z.TranslateService}]}});class Pr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.deduplicationStrategie=Gt,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Dt}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[ie(e?.interval)?e.interval:null,[O.required,O.min(1)]],strategy:[ie(e?.strategy)?e.strategy:null,[O.required]],outMsgType:[ie(e?.outMsgType)?e.outMsgType:null,[O.required]],maxPendingMsgs:[ie(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[O.required,O.min(1),O.max(1e3)]],maxRetries:[ie(e?.maxRetries)?e.maxRetries:null,[O.required,O.min(0),O.max(100)]]})}prepareInputConfig(e){return e||(e={}),e.outMsgType||(e.outMsgType="POST_TELEMETRY_REQUEST"),super.prepareInputConfig(e)}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}):this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}}e("DeduplicationConfigComponent",Pr),Pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,deps:[{token:P.Store},{token:R.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Pr,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:H.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:H.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ue.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:J.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:Y.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:Y.MatLabel,selector:"mat-label"},{kind:"directive",type:Y.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Y.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ce.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Je.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Je.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Je.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:R.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:R.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:Z.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:Xe.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:et.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Rn,selector:"tb-output-message-type-autocomplete",inputs:["subscriptSizing","disabled","required"]},{kind:"component",type:Nn,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,decorators:[{type:n,args:[{selector:"tb-action-node-msg-deduplication-config",template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.FormBuilder}]}});class Rr{}e("RulenodeCoreConfigTransformModule",Rr),Rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Rr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Rr,declarations:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr],imports:[$,M,Un],exports:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr]}),Rr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,decorators:[{type:c,args:[{declarations:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr],imports:[$,M,Un],exports:[Tr,Ir,Er,Gr,Dr,wr,Vr,Pr]}]}]});class Or extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=F}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[O.required]]})}}e("RuleChainInputComponent",Or),Or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Or,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:lt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:R.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class _r extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",_r),_r.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,deps:[{token:P.Store},{token:R.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_r.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_r,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:W.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:R.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:R.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:Z.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:P.Store},{type:R.UntypedFormBuilder}]}});class Br{}e("RuleNodeCoreConfigFlowModule",Br),Br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Br,declarations:[Or,_r],imports:[$,M,Un],exports:[Or,_r]}),Br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,imports:[$,M,Un]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,decorators:[{type:c,args:[{declarations:[Or,_r],imports:[$,M,Un],exports:[Or,_r]}]}]});class Kr{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","advanced-settings":"Advanced settings","create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","copy-message-type":"Copy message type","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","message-type-value":"Message type value","message-type-value-required":"Message type value is required","message-type-value-max-length":"Message type value should be less than 256","output-message-type":"Output message type","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Force notification to the device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-on-update-hint":"If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.","notify-device-on-delete-hint":"If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.","latest-timeseries":"Latest time-series data keys","timeseries-keys":"Timeseries keys","timeseries-keys-required":"At least one timeseries key should be selected.","add-timeseries-key":"Add timeseries key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","current-key-name":"Current key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","relations-query-config-direction-suffix":"originator","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","new-originator":"New originator","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related entity","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity by name pattern","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From","from-template-required":"From is required","message-to-metadata":"Message to metadata","metadata-to-message":"Metadata to message","from-message":"From message","from-metadata":"From metadata","to-template":"To","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc","bcc-template":"Bcc","subject-template":"Subject","subject-template-required":"Subject Template is required","body-template":"Body","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","body-type-template":"Body type template","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","email-sender":"Email sender","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time-series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","add-device-profile":"Add device profile","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"timeseries key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch timeseries from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch timeseries invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required","keys-mapping":"keys mapping","add-key":"Add key",recipients:"Recipients","message-subject-and-content":"Message subject and content","template-rules-hint":"Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","originator-customer-desc":"Use customer of incoming message originator as new originator.","originator-tenant-desc":"Use current tenant as new originator.","originator-related-entity-desc":"Use related entity as new originator. Lookup based on configured relation type and direction.","originator-alarm-originator-desc":"Use alarm originator as new originator. Only if incoming message originator is alarm entity.","originator-entity-by-name-pattern-desc":"Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.","email-from-template-hint":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","recipients-block-main-hint":"Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata."},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","copy-key-values-from":"Copy key-values from","delete-key-values":"Delete key-values","delete-key-values-from":"Delete key-values from","at-least-one-key-error":"At least one key should be selected.","unique-key-value-pair-error":"'{{keyText}}' must be different from the '{{valText}}'!"},"mail-body-type":{"plain-text":"Plain text",html:"HTML",dynamic:"Dynamic","use-body-type-template":"Use body type template","plain-text-description":"Simple, unformatted text with no special styling or formating.","html-text-description":"Allows you to use HTML tags for formatting, links and images in your mai body.","dynamic-text-description":"Allows to use Plain Text or HTML body type dynamically based on templatization feature.","after-template-evaluation-hint":"After template evaluation value should be true for HTML, and false for Plain text."}}},!0)}(e)}}e("RuleNodeCoreConfigModule",Kr),Kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,deps:[{token:Z.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),Kr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Kr,declarations:[dt],imports:[$,M],exports:[Hn,Lr,nr,gr,Rr,Br,dt]}),Kr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,imports:[$,M,Hn,Lr,nr,gr,Rr,Br]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,decorators:[{type:c,args:[{declarations:[dt],imports:[$,M],exports:[Hn,Lr,nr,gr,Rr,Br,dt]}]}],ctorParameters:function(){return[{type:Z.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java new file mode 100644 index 0000000000..804d30d28f --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.rule.engine.geo; + +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class GpsGeofencingActionTestCase { + + private EntityId entityId; + private Map entityStates; + private boolean msgInside; + private boolean reportPresenceStatusOnEachMessage; + + public GpsGeofencingActionTestCase(EntityId entityId, boolean msgInside, boolean reportPresenceStatusOnEachMessage, EntityGeofencingState entityGeofencingState) { + this.entityId = entityId; + this.msgInside = msgInside; + this.reportPresenceStatusOnEachMessage = reportPresenceStatusOnEachMessage; + this.entityStates = new HashMap<>(); + this.entityStates.put(entityId, entityGeofencingState); + } +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java new file mode 100644 index 0000000000..5cd7e02c59 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -0,0 +1,259 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.rule.engine.geo; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.attributes.AttributesService; + +import java.time.Duration; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.ENTERED; +import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.INSIDE; +import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.LEFT; +import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE; +import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCESS; + +@ExtendWith(MockitoExtension.class) +class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { + + @Mock + private TbContext ctx; + @Mock + private AttributesService attributesService; + private TbGpsGeofencingActionNode node; + + @BeforeEach + void setUp() { + node = spy(new TbGpsGeofencingActionNode()); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + private static Stream givenReportPresenceStatusOnEachMessage_whenOnMsg_thenVerifyOutputMsgType() { + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + long tsNow = System.currentTimeMillis(); + long tsNowMinusMinuteAndMillis = tsNow - Duration.ofMinutes(1).plusMillis(1).toMillis(); + return Stream.of( + // default config with presenceMonitoringStrategyOnEachMessage false and msgInside true + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false, + new EntityGeofencingState(false, 0, false)), ENTERED), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false, + new EntityGeofencingState(true, tsNow, false)), SUCCESS), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false, + new EntityGeofencingState(true, tsNowMinusMinuteAndMillis, false)), INSIDE), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, false, + new EntityGeofencingState(true, tsNow, true)), SUCCESS), + // default config with presenceMonitoringStrategyOnEachMessage false and msgInside false + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false, + new EntityGeofencingState(false, 0, false)), LEFT), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false, + new EntityGeofencingState(false, tsNow, false)), SUCCESS), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false, + new EntityGeofencingState(false, tsNowMinusMinuteAndMillis, false)), OUTSIDE), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, false, + new EntityGeofencingState(false, tsNow, true)), SUCCESS), + // default config with presenceMonitoringStrategyOnEachMessage true and msgInside true + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true, + new EntityGeofencingState(false, 0, false)), ENTERED), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true, + new EntityGeofencingState(true, tsNow, false)), INSIDE), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, true, true, + new EntityGeofencingState(true, tsNowMinusMinuteAndMillis, false)), INSIDE), + // default config with presenceMonitoringStrategyOnEachMessage true and msgInside false + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true, + new EntityGeofencingState(false, 0, false)), LEFT), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true, + new EntityGeofencingState(false, tsNow, false)), OUTSIDE), + Arguments.of(new GpsGeofencingActionTestCase(deviceId, false, true, + new EntityGeofencingState(false, tsNowMinusMinuteAndMillis, false)), OUTSIDE) + ); + } + + @ParameterizedTest + @MethodSource + void givenReportPresenceStatusOnEachMessage_whenOnMsg_thenVerifyOutputMsgType( + GpsGeofencingActionTestCase gpsGeofencingActionTestCase, + String expectedOutput + ) throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingActionNodeConfiguration().defaultConfiguration(); + config.setReportPresenceStatusOnEachMessage(gpsGeofencingActionTestCase.isReportPresenceStatusOnEachMessage()); + + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + TbMsg msg = gpsGeofencingActionTestCase.isMsgInside() ? + getInsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()) : + getOutsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()); + + when(ctx.getAttributesService()).thenReturn(attributesService); + + ReflectionTestUtils.setField(node, "entityStates", gpsGeofencingActionTestCase.getEntityStates()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + verify(ctx.getAttributesService(), never()).find(any(), any(), any(), anyString()); + verify(ctx, never()).tellFailure(any(), any(Throwable.class)); + verify(ctx, never()).enqueueForTellNext(any(), eq(expectedOutput), any(), any()); + verify(ctx, never()).ack(any()); + + if (SUCCESS.equals(expectedOutput)) { + verify(ctx).tellSuccess(eq(msg)); + } else { + verify(ctx).tellNext(eq(msg), eq(expectedOutput)); + } + } + + private TbMsg getOutsideRectangleTbMsg(EntityId entityId) { + return getTbMsg(entityId, getMetadataForNewVersionPolygonPerimeter(), + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + } + + private TbMsg getInsideRectangleTbMsg(EntityId entityId) { + return getTbMsg(entityId, getMetadataForNewVersionPolygonPerimeter(), + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + } + + private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { + String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + } + + private TbMsgMetaData getMetadataForNewVersionPolygonPerimeter() { + var metadata = new TbMsgMetaData(); + metadata.putValue("ss_perimeter", GeoUtilTest.SIMPLE_RECT); + return metadata; + } + + // Rule nodes upgrade + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { + return Stream.of( + // default config for version 0 + Arguments.of(0, + "{\n" + + " \"minInsideDuration\": 1,\n" + + " \"minOutsideDuration\": 1,\n" + + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"latitudeKeyName\": \"latitude\",\n" + + " \"longitudeKeyName\": \"longitude\",\n" + + " \"perimeterType\": \"POLYGON\",\n" + + " \"fetchPerimeterInfoFromMessageMetadata\": true,\n" + + " \"perimeterKeyName\": \"ss_perimeter\",\n" + + " \"polygonsDefinition\": null,\n" + + " \"centerLatitude\": null,\n" + + " \"centerLongitude\": null,\n" + + " \"range\": null,\n" + + " \"rangeUnit\": null\n" + + "}\n", + true, + "{\n" + + " \"minInsideDuration\": 1,\n" + + " \"minOutsideDuration\": 1,\n" + + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + + " \"latitudeKeyName\": \"latitude\",\n" + + " \"longitudeKeyName\": \"longitude\",\n" + + " \"perimeterType\": \"POLYGON\",\n" + + " \"fetchPerimeterInfoFromMessageMetadata\": true,\n" + + " \"perimeterKeyName\": \"ss_perimeter\",\n" + + " \"polygonsDefinition\": null,\n" + + " \"centerLatitude\": null,\n" + + " \"centerLongitude\": null,\n" + + " \"range\": null,\n" + + " \"rangeUnit\": null\n" + + "}\n"), + // default config for version 1 with upgrade from version 0 + Arguments.of(0, + "{\n" + + " \"minInsideDuration\": 1,\n" + + " \"minOutsideDuration\": 1,\n" + + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + + " \"latitudeKeyName\": \"latitude\",\n" + + " \"longitudeKeyName\": \"longitude\",\n" + + " \"perimeterType\": \"POLYGON\",\n" + + " \"fetchPerimeterInfoFromMessageMetadata\": true,\n" + + " \"perimeterKeyName\": \"ss_perimeter\",\n" + + " \"polygonsDefinition\": null,\n" + + " \"centerLatitude\": null,\n" + + " \"centerLongitude\": null,\n" + + " \"range\": null,\n" + + " \"rangeUnit\": null\n" + + "}\n", + false, + "{\n" + + " \"minInsideDuration\": 1,\n" + + " \"minOutsideDuration\": 1,\n" + + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + + " \"latitudeKeyName\": \"latitude\",\n" + + " \"longitudeKeyName\": \"longitude\",\n" + + " \"perimeterType\": \"POLYGON\",\n" + + " \"fetchPerimeterInfoFromMessageMetadata\": true,\n" + + " \"perimeterKeyName\": \"ss_perimeter\",\n" + + " \"polygonsDefinition\": null,\n" + + " \"centerLatitude\": null,\n" + + " \"centerLongitude\": null,\n" + + " \"range\": null,\n" + + " \"rangeUnit\": null\n" + + "}\n") + ); + } + + @Override + protected TbNode getTestNode() { + return node; + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java index 5967023354..f3e61dcffc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java @@ -37,10 +37,12 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; @@ -52,6 +54,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -95,6 +98,8 @@ public class EntitiesFieldsAsyncLoaderTest { private RuleChainService ruleChainServiceMock; @Mock private EntityViewService entityViewServiceMock; + @Mock + private EdgeService edgeServiceMock; @BeforeAll public static void setup() { @@ -108,7 +113,8 @@ public class EntitiesFieldsAsyncLoaderTest { EntityType.DEVICE, EntityType.ALARM, EntityType.RULE_CHAIN, - EntityType.ENTITY_VIEW + EntityType.ENTITY_VIEW, + EntityType.EDGE ); } @@ -228,6 +234,14 @@ public class EntitiesFieldsAsyncLoaderTest { when(ctxMock.getEntityViewService()).thenReturn(entityViewServiceMock); doReturn(entityView).when(entityViewServiceMock).findEntityViewByIdAsync(eq(TENANT_ID), any()); + break; + case EDGE: + var edge = Futures.immediateFuture(entityDoesNotExist ? null : new Edge(new EdgeId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getEdgeService()).thenReturn(edgeServiceMock); + doReturn(edge).when(edgeServiceMock).findEdgeByIdAsync(eq(TENANT_ID), any()); + break; default: throw new RuntimeException("Unexpected EntityType: " + entityType); @@ -252,6 +266,8 @@ public class EntitiesFieldsAsyncLoaderTest { return new RuleChain((RuleChainId) entityId); case ENTITY_VIEW: return new EntityView((EntityViewId) entityId); + case EDGE: + return new Edge((EdgeId) entityId); default: throw new RuntimeException("Unexpected EntityType: " + entityId.getEntityType()); } diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 805ab5a8dd..a40374e1f6 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -128,14 +128,18 @@ transport: bind_port: "${SNMP_BIND_PORT:1620}" response_processing: # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices - parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}" + parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:4}" # to configure SNMP to work over UDP or TCP underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}" - # Batch size to request OID mappings from the device (useful when the device profile has multiple hundreds of OID mappings) + # Maximum size of a PDU (amount of OID mappings in a single SNMP request). The request will be split into multiple PDUs if mappings amount exceeds this number max_request_oids: "${SNMP_MAX_REQUEST_OIDS:100}" + # Delay after sending each request chunk (in case the request was split into multiple PDUs due to max_request_oids) + request_chunk_delay_ms: "${SNMP_REQUEST_CHUNK_DELAY_MS:100}" response: # To ignore SNMP response values that do not match the data type of the configured OID mapping (by default false - will throw an error if any value of the response not match configured data types) ignore_type_cast_errors: "${SNMP_RESPONSE_IGNORE_TYPE_CAST_ERRORS:false}" + # Thread pool size for scheduler that executes device querying tasks + scheduler_thread_pool_size: "${SNMP_SCHEDULER_THREAD_POOL_SIZE:4}" sessions: # Session inactivity timeout is a global configuration parameter that defines how long the device transport session will be opened after the last message arrives from the device. # The parameter value is in milliseconds. diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 4bbe817fcf..ab60ebd525 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -44,6 +44,7 @@ "@ngrx/store-devtools": "^15.4.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", + "@svgdotjs/svg.filter.js": "^3.0.8", "@svgdotjs/svg.js": "^3.2.0", "@tinymce/tinymce-angular": "^7.0.0", "ace-builds": "1.4.13", diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 548cbc7041..4b21a60b30 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -82,6 +82,7 @@ export interface RpcApi { export interface IWidgetUtils { formatValue: (value: any, dec?: number, units?: string, showZeroDecimals?: boolean) => string | undefined; + getEntityDetailsPageURL: (id: string, entityType: EntityType) => string; } export interface WidgetActionsApi { @@ -91,6 +92,7 @@ export interface WidgetActionsApi { entityId?: EntityId, entityName?: string, additionalParams?: any, entityLabel?: string) => void; elementClick: ($event: Event) => void; cardClick: ($event: Event) => void; + click: ($event: Event) => void; getActiveEntityInfo: () => SubscriptionEntityInfo; openDashboardStateInSeparateDialog: (targetDashboardStateId: string, params?: StateParams, dialogTitle?: string, hideDashboardToolbar?: boolean, dialogWidth?: number, dialogHeight?: number) => void; diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index 8df64be126..2339801e2b 100644 --- a/ui-ngx/src/app/core/http/image.service.ts +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; -import { Observable, of } from 'rxjs'; +import { Observable, of, ReplaySubject } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { NO_IMAGE_DATA_URI, @@ -36,6 +36,9 @@ import { ResourcesService } from '@core/services/resources.service'; providedIn: 'root' }) export class ImageService { + + private imagesLoading: { [url: string]: ReplaySubject } = {}; + constructor( private http: HttpClient, private sanitizer: DomSanitizer, @@ -95,12 +98,34 @@ export class ImageService { parts[parts.length - 1] = encodeURIComponent(key); const encodedUrl = parts.join('/'); const imageLink = preview ? (encodedUrl + '/preview') : encodedUrl; - const options = defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}); - return this.http - .get(imageLink, {...options, ...{ responseType: 'blob' } }).pipe( + return this.loadImageDataUrl(imageLink, asString, emptyUrl); + } + + private loadImageDataUrl(imageLink: string, asString = false, emptyUrl = NO_IMAGE_DATA_URI): Observable { + let request: ReplaySubject; + if (this.imagesLoading[imageLink]) { + request = this.imagesLoading[imageLink]; + } else { + request = new ReplaySubject(1); + this.imagesLoading[imageLink] = request; + const options = defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}); + this.http.get(imageLink, {...options, ...{ responseType: 'blob' } }).subscribe({ + next: (value) => { + request.next(value); + request.complete(); + }, + error: err => { + request.error(err); + }, + complete: () => { + delete this.imagesLoading[imageLink]; + } + }); + } + return request.pipe( switchMap(val => blobToBase64(val).pipe( - map((dataUrl) => asString ? dataUrl : this.sanitizer.bypassSecurityTrustUrl(dataUrl)) - )), + map((dataUrl) => asString ? dataUrl : this.sanitizer.bypassSecurityTrustUrl(dataUrl)) + )), catchError(() => of(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl))) ); } diff --git a/ui-ngx/src/app/core/notification/notification.models.ts b/ui-ngx/src/app/core/notification/notification.models.ts index 1d658a5b9e..37e2177d62 100644 --- a/ui-ngx/src/app/core/notification/notification.models.ts +++ b/ui-ngx/src/app/core/notification/notification.models.ts @@ -33,6 +33,7 @@ export class NotificationMessage { horizontalPosition?: NotificationHorizontalPosition; verticalPosition?: NotificationVerticalPosition; panelClass?: string | string[]; + modern?: boolean; } export class HideNotification { diff --git a/ui-ngx/src/app/core/services/item-buffer.service.ts b/ui-ngx/src/app/core/services/item-buffer.service.ts index eeade82898..e1941590ea 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -27,7 +27,7 @@ import { widgetType } from '@shared/models/widget.models'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; -import { deepClone, isEqual } from '@core/utils'; +import { deepClone, isDefinedAndNotNull, isEqual } from '@core/utils'; import { UtilsService } from '@core/services/utils.service'; import { Observable, of, throwError } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -309,6 +309,12 @@ export class ItemBufferService { if (origNode.error) { node.error = origNode.error; } + if (isDefinedAndNotNull(origNode.singletonMode)) { + node.singletonMode = origNode.singletonMode; + } + if (isDefinedAndNotNull(origNode.queueName)) { + node.queueName = origNode.queueName; + } ruleNodes.nodes.push(node); if (i === 0) { top = node.y; diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index 440f9c1d3d..70b04ee4eb 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -17,7 +17,7 @@ // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -import { Inject, Injectable, NgZone } from '@angular/core'; +import { Inject, Injectable, NgZone, Renderer2 } from '@angular/core'; import { WINDOW } from '@core/services/window.service'; import { ExceptionData } from '@app/shared/models/error.models'; import { @@ -55,8 +55,9 @@ import { TelemetryType } from '@shared/models/telemetry/telemetry.models'; import { EntityId } from '@shared/models/id/entity-id'; -import { DatePipe } from '@angular/common'; +import { DatePipe, DOCUMENT } from '@angular/common'; import { entityTypeTranslations } from '@shared/models/entity-type.models'; +import cssjs from '@core/css/css'; const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); @@ -116,6 +117,7 @@ export class UtilsService { defaultAlarmDataKeys: Array = []; constructor(@Inject(WINDOW) private window: Window, + @Inject(DOCUMENT) private document: Document, private zone: NgZone, private datePipe: DatePipe, private translate: TranslateService) { @@ -502,4 +504,24 @@ export class UtilsService { return base64toObj(b64Encoded); } + public applyCssToElement(renderer: Renderer2, element: any, cssClassPrefix: string, css: string): string { + const cssParser = new cssjs(); + cssParser.testMode = false; + const cssClass = `${cssClassPrefix}-${guid()}`; + cssParser.cssPreviewNamespace = cssClass; + cssParser.createStyleElement(cssClass, css); + renderer.addClass(element, cssClass); + return cssClass; + } + + public clearCssElement(renderer: Renderer2, cssClass: string, element?: any): void { + if (element) { + renderer.removeClass(element, cssClass); + } + const el = this.document.getElementById(cssClass); + if (el) { + el.parentNode.removeChild(el); + } + } + } diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index c19b96e2a7..fcb4e0b411 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -14,6 +14,8 @@ /// limitations under the License. /// +/* eslint-disable max-len */ + import * as AngularAnimations from '@angular/animations'; import * as AngularCore from '@angular/core'; import * as AngularCommon from '@angular/common'; @@ -192,6 +194,17 @@ import * as ScrollGridComponent from '@shared/components/grid/scroll-grid.compon import * as GalleryImageInputComponent from '@shared/components/image/gallery-image-input.component'; import * as MultipleGalleryImageInputComponent from '@shared/components/image/multiple-gallery-image-input.component'; +import * as CssUnitSelectComponent from '@home/components/widget/lib/settings/common/css-unit-select.component'; +import * as WidgetActionsPanelComponent from '@home/components/widget/config/basic/common/widget-actions-panel.component'; +import * as FontSettingsComponent from '@home/components/widget/lib/settings/common/font-settings.component'; +import * as ColorSettingsComponent from '@home/components/widget/lib/settings/common/color-settings.component'; +import * as DisplayColumnsPanelComponent from '@home/components/widget/lib/display-columns-panel.component'; +import * as AlarmDetailsDialogComponent from '@home/components/alarm/alarm-details-dialog.component'; +import * as AlarmAssigneePanelComponent from '@home/components/alarm/alarm-assignee-panel.component'; +import * as AlarmCommentDialogComponent from '@home/components/alarm/alarm-comment-dialog.component'; +import * as AlarmFilterConfigComponent from '@home/components/alarm/alarm-filter-config.component'; +import * as DatasourceComponent from '@home/components/widget/config/datasources.component'; +import * as DataKeysPanelComponent from '@home/components/widget/config/basic/common/data-keys-panel.component'; import * as AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.component'; import * as EntitiesTableComponent from '@home/components/entity/entities-table.component'; import * as DetailsPanelComponent from '@home/components/details-panel.component'; @@ -226,9 +239,9 @@ import * as DataKeyConfigComponent from '@home/components/widget/config/data-key import * as LegendConfigComponent from '@home/components/widget/lib/settings/common/legend-config.component'; import * as ManageWidgetActionsComponent from '@home/components/widget/action/manage-widget-actions.component'; import * as WidgetActionDialogComponent from '@home/components/widget/action/widget-action-dialog.component'; -import * as CustomActionPrettyResourcesTabsComponent from '@home/components/widget/action/custom-action-pretty-resources-tabs.component'; -import * as CustomActionPrettyEditorComponent from '@home/components/widget/action/custom-action-pretty-editor.component'; -import * as MobileActionEditorComponent from '@home/components/widget/action/mobile-action-editor.component'; +import * as CustomActionPrettyResourcesTabsComponent from '@home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component'; +import * as CustomActionPrettyEditorComponent from '@home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component'; +import * as MobileActionEditorComponent from '@home/components/widget/lib/settings/common/action/mobile-action-editor.component'; import * as CustomDialogService from '@home/components/widget/dialog/custom-dialog.service'; import * as CustomDialogContainerComponent from '@home/components/widget/dialog/custom-dialog-container.component'; import * as ImportDialogComponent from '@shared/import-export/import-dialog.component'; @@ -237,6 +250,10 @@ import * as ImportDialogCsvComponent from '@shared/import-export/import-dialog-c import * as TableColumnsAssignmentComponent from '@shared/import-export/table-columns-assignment.component'; import * as EventContentDialogComponent from '@home/components/event/event-content-dialog.component'; import * as SharedHomeComponentsModule from '@home/components/shared-home-components.module'; +import * as WidgetConfigComponentsModule from '@home/components/widget/config/widget-config-components.module'; +import * as BasicWidgetConfigModule from '@home/components/widget/config/basic/basic-widget-config.module'; +import * as WidgetSettingsCommonModule from '@home/components/widget/lib/settings/common/widget-settings-common.module'; +import * as WidgetComponentsModule from '@home/components/widget/widget-components.module'; import * as SelectTargetLayoutDialogComponent from '@home/components/dashboard/select-target-layout-dialog.component'; import * as SelectTargetStateDialogComponent from '@home/components/dashboard/select-target-state-dialog.component'; import * as AliasesEntityAutocompleteComponent from '@home/components/alias/aliases-entity-autocomplete.component'; @@ -261,7 +278,6 @@ import * as FilterPredicateValueComponent from '@home/components/filter/filter-p import * as TenantProfileComponent from '@home/components/profile/tenant-profile.component'; import * as TenantProfileDialogComponent from '@home/components/profile/tenant-profile-dialog.component'; import * as TenantProfileDataComponent from '@home/components/profile/tenant-profile-data.component'; -// eslint-disable-next-line max-len import * as DefaultDeviceProfileConfigurationComponent from '@home/components/profile/device/default-device-profile-configuration.component'; import * as DeviceProfileConfigurationComponent from '@home/components/profile/device/device-profile-configuration.component'; import * as DeviceProfileComponent from '@home/components/profile/device-profile.component'; @@ -286,7 +302,6 @@ import * as AlarmScheduleInfoComponent from '@home/components/profile/alarm/alar import * as AlarmScheduleDialogComponent from '@home/components/profile/alarm/alarm-schedule-dialog.component'; import * as EditAlarmDetailsDialogComponent from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; import * as AlarmRuleConditionDialogComponent from '@home/components/profile/alarm/alarm-rule-condition-dialog.component'; -// eslint-disable-next-line max-len import * as DefaultTenantProfileConfigurationComponent from '@home/components/profile/tenant/default-tenant-profile-configuration.component'; import * as TenantProfileConfigurationComponent from '@home/components/profile/tenant/tenant-profile-configuration.component'; import * as SmsProviderConfigurationComponent from '@home/components/sms/sms-provider-configuration.component'; @@ -507,6 +522,17 @@ class ModulesMap implements IModulesMap { '@shared/components/image/gallery-image-input.component': GalleryImageInputComponent, '@shared/components/image/multiple-gallery-image-input.component': MultipleGalleryImageInputComponent, + '@home/components/alarm/alarm-filter-config.component': AlarmFilterConfigComponent, + '@home/components/alarm/alarm-comment-dialog.component': AlarmCommentDialogComponent, + '@home/components/alarm/alarm-assignee-panel.component': AlarmAssigneePanelComponent, + '@home/components/alarm/alarm-details-dialog.component': AlarmDetailsDialogComponent, + '@home/components/widget/lib/display-columns-panel.component': DisplayColumnsPanelComponent, + '@home/components/widget/config/datasources.component': DatasourceComponent, + '@home/components/widget/config/basic/common/data-keys-panel.component': DataKeysPanelComponent, + '@home/components/widget/lib/settings/common/color-settings.component': ColorSettingsComponent, + '@home/components/widget/lib/settings/common/font-settings.component': FontSettingsComponent, + '@home/components/widget/config/basic/common/widget-actions-panel.component': WidgetActionsPanelComponent, + '@home/components/widget/lib/settings/common/css-unit-select.component': CssUnitSelectComponent, '@home/components/entity/add-entity-dialog.component': AddEntityDialogComponent, '@home/components/entity/entities-table.component': EntitiesTableComponent, '@home/components/details-panel.component': DetailsPanelComponent, @@ -541,14 +567,18 @@ class ModulesMap implements IModulesMap { '@home/components/widget/lib/settings/common/legend-config.component': LegendConfigComponent, '@home/components/widget/action/manage-widget-actions.component': ManageWidgetActionsComponent, '@home/components/widget/action/widget-action-dialog.component': WidgetActionDialogComponent, - '@home/components/widget/action/custom-action-pretty-resources-tabs.component': CustomActionPrettyResourcesTabsComponent, - '@home/components/widget/action/custom-action-pretty-editor.component': CustomActionPrettyEditorComponent, - '@home/components/widget/action/mobile-action-editor.component': MobileActionEditorComponent, + '@home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component': CustomActionPrettyResourcesTabsComponent, + '@home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component': CustomActionPrettyEditorComponent, + '@home/components/widget/lib/settings/common/action/mobile-action-editor.component': MobileActionEditorComponent, '@home/components/widget/dialog/custom-dialog.service': CustomDialogService, '@home/components/widget/dialog/custom-dialog-container.component': CustomDialogContainerComponent, '@home/components/attribute/add-widget-to-dashboard-dialog.component': AddWidgetToDashboardDialogComponent, '@home/components/event/event-content-dialog.component': EventContentDialogComponent, '@home/components/shared-home-components.module': SharedHomeComponentsModule, + '@home/components/widget/config/widget-config-components.module': WidgetConfigComponentsModule, + '@home/components/widget/config/basic/basic-widget-config.module': BasicWidgetConfigModule, + '@home/components/widget/lib/settings/common/widget-settings-common.module': WidgetSettingsCommonModule, + '@home/components/widget/widget-components.module': WidgetComponentsModule, '@home/components/dashboard/select-target-layout-dialog.component': SelectTargetLayoutDialogComponent, '@home/components/dashboard/select-target-state-dialog.component': SelectTargetStateDialogComponent, '@home/components/alias/aliases-entity-autocomplete.component': AliasesEntityAutocompleteComponent, diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index f873743340..fe6364cc09 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -1049,26 +1049,20 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.onAddWidgetClosed(); this.isAddingWidgetClosed = true; } - if (this.widgetEditMode) { - if (revert) { - this.dashboard = this.prevDashboard; - this.dashboardLogoCache = undefined; - this.dashboardConfiguration = this.dashboard.configuration; - } - } else { - this.resetHighlight(); - if (revert) { - this.dashboard = this.prevDashboard; - this.dashboardLogoCache = undefined; - this.dashboardConfiguration = this.dashboard.configuration; + this.resetHighlight(); + if (revert) { + this.dashboard = this.prevDashboard; + this.dashboardLogoCache = undefined; + this.dashboardConfiguration = this.dashboard.configuration; + if (!this.widgetEditMode) { this.dashboardCtx.dashboardTimewindow = this.dashboardConfiguration.timewindow; this.updateDashboardCss(); this.entityAliasesUpdated(); this.filtersUpdated(); this.updateLayouts(); - } else { - this.dashboard.configuration.timewindow = this.dashboardCtx.dashboardTimewindow; } + } else if (!this.widgetEditMode) { + this.dashboard.configuration.timewindow = this.dashboardCtx.dashboardTimewindow; } } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html index 5ca942f33c..c69a2a4de9 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html @@ -71,6 +71,7 @@ [dashboardStyle]="dashboardStyle" [backgroundImage]="backgroundImage" [isEdit]="isEdit" + [isPreview]="isPreview" [isMobile]="isMobileSize" [isEditActionEnabled]="isEditActionEnabled" [isExportActionEnabled]="isExportActionEnabled" diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts index 469630c212..6cc1ec7dbe 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts @@ -97,6 +97,9 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo @Input() isEdit: boolean; + @Input() + isPreview: boolean; + @Input() autofillHeight: boolean; @@ -211,7 +214,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo disableAutoPositionOnConflict: false, pushItems: false, swap: false, - maxRows: 100, + maxRows: 3000, minCols: this.columns ? this.columns : 24, maxCols: 3000, maxItemCols: 1000, @@ -290,12 +293,12 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo this.dashboardTimewindowChangedSubject.next(this.dashboardTimewindow); } - if (updateMobileOpts) { - this.updateMobileOpts(); - } if (updateLayoutOpts) { this.updateLayoutOpts(); } + if (updateMobileOpts) { + this.updateMobileOpts(); + } if (updateEditingOpts) { this.updateEditingOpts(); } diff --git a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts index 080fd3e576..a37ae90dc0 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filters-edit.component.ts @@ -119,7 +119,7 @@ export class FiltersEditComponent implements OnInit, OnDestroy { const filteredArray = Object.entries(this.filtersInfo); if (filteredArray.length === 1) { - const singleFilter: Filter = {id: filteredArray[0][0], ...filteredArray[0][1]}; + const singleFilter: Filter = {id: filteredArray[0][0], ...deepClone(filteredArray[0][1])}; this.dialog.open(UserFilterDialogComponent, { disableClose: true, diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 4694b5ead8..5390a915d3 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -46,9 +46,6 @@ import { EntityFilterComponent } from '@home/components/entity/entity-filter.com import { RelationFiltersComponent } from '@home/components/relation/relation-filters.component'; import { ManageWidgetActionsComponent } from '@home/components/widget/action/manage-widget-actions.component'; import { WidgetActionDialogComponent } from '@home/components/widget/action/widget-action-dialog.component'; -import { CustomActionPrettyResourcesTabsComponent } from '@home/components/widget/action/custom-action-pretty-resources-tabs.component'; -import { CustomActionPrettyEditorComponent } from '@home/components/widget/action/custom-action-pretty-editor.component'; -import { MobileActionEditorComponent } from '@home/components/widget/action/mobile-action-editor.component'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; import { CustomDialogContainerComponent } from '@home/components/widget/dialog/custom-dialog-container.component'; import { AddWidgetToDashboardDialogComponent } from '@home/components/attribute/add-widget-to-dashboard-dialog.component'; @@ -218,9 +215,6 @@ import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delet ManageWidgetActionsComponent, WidgetActionDialogComponent, ManageWidgetActionsDialogComponent, - CustomActionPrettyResourcesTabsComponent, - CustomActionPrettyEditorComponent, - MobileActionEditorComponent, CustomDialogContainerComponent, SelectTargetLayoutDialogComponent, SelectTargetStateDialogComponent, @@ -359,9 +353,6 @@ import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delet ManageWidgetActionsComponent, WidgetActionDialogComponent, ManageWidgetActionsDialogComponent, - CustomActionPrettyResourcesTabsComponent, - CustomActionPrettyEditorComponent, - MobileActionEditorComponent, CustomDialogContainerComponent, SelectTargetLayoutDialogComponent, SelectTargetStateDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html index 1b1d97ae06..de80b9f238 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.html @@ -19,7 +19,7 @@
-
+
device-profile.snmp.scope diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.scss index 027e82c89c..e7877699b5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-communication-config.component.scss @@ -13,12 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import '../scss/constants'; + :host { .communication-config { border: 2px groove rgba(0, 0, 0, 0.25); border-radius: 4px; padding: 8px; min-width: 0; + flex-direction: column; + display: flex; + place-content: stretch flex-start; + align-items: stretch; + flex: 1; + gap: 0; } .scope-row { @@ -28,6 +36,13 @@ .required-text { margin: 16px 0 } + + @media #{$mat-gt-xmd} { + .communication-config { + flex-direction: row; + gap: 8px; + } + } } :host ::ng-deep { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html index f56bfa9255..8b4b656c02 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.html @@ -19,8 +19,8 @@
- - + +
@@ -28,7 +28,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.scss index d9ad3d7923..01ff5a71e2 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp/snmp-device-profile-mapping.component.scss @@ -15,13 +15,9 @@ */ :host { .mapping-config { - min-width: 518px; - } - .mapping-list { - padding-bottom: 8px; - } - .required-text { - margin: 14px 0; + .required-text { + margin: 14px 0; + } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 4c309f5e80..867cd74f3b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -523,7 +523,7 @@ -
+
@@ -531,7 +531,7 @@ [type]="rateLimitsType.DEVICE_TELEMETRY_DATA_POINTS">
-
+
@@ -539,7 +539,7 @@ [type]="rateLimitsType.CUSTOMER_SERVER_REST_LIMITS_CONFIGURATION">
-
+
@@ -547,7 +547,7 @@ [type]="rateLimitsType.TENANT_ENTITY_IMPORT_RATE_LIMIT">
-
+
@@ -555,7 +555,7 @@ [type]="rateLimitsType.CASSANDRA_QUERY_TENANT_RATE_LIMITS_CONFIGURATION">
-
+
@@ -563,6 +563,14 @@ [type]="rateLimitsType.TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT">
+
+ + + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index a0b8770509..0c95324d85 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -106,7 +106,9 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxWsSubscriptionsPerRegularUser: [null, [Validators.min(0)]], maxWsSubscriptionsPerPublicUser: [null, [Validators.min(0)]], wsUpdatesPerSessionRateLimit: [null, []], - cassandraQueryTenantRateLimitsConfiguration: [null, []] + cassandraQueryTenantRateLimitsConfiguration: [null, []], + edgeEventRateLimits: [null, []], + edgeEventRateLimitsPerEdge: [null, []] }); this.defaultTenantProfileConfigurationFormGroup.get('smsEnabled').valueChanges.pipe( diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts index 257752f5f4..8924682aa1 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts @@ -35,7 +35,9 @@ export enum RateLimitsType { TENANT_ENTITY_EXPORT_RATE_LIMIT = 'TENANT_ENTITY_EXPORT_RATE_LIMIT', TENANT_ENTITY_IMPORT_RATE_LIMIT = 'TENANT_ENTITY_IMPORT_RATE_LIMIT', TENANT_NOTIFICATION_REQUEST_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUEST_RATE_LIMIT', - TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT' + TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT', + EDGE_EVENTS_RATE_LIMIT = 'EDGE_EVENTS_RATE_LIMIT', + EDGE_EVENTS_PER_EDGE_RATE_LIMIT = 'EDGE_EVENTS_PER_EDGE_RATE_LIMIT' } export const rateLimitsLabelTranslationMap = new Map( @@ -54,6 +56,8 @@ export const rateLimitsLabelTranslationMap = new Map( [RateLimitsType.TENANT_ENTITY_IMPORT_RATE_LIMIT, 'tenant-profile.tenant-entity-import-rate-limit'], [RateLimitsType.TENANT_NOTIFICATION_REQUEST_RATE_LIMIT, 'tenant-profile.tenant-notification-request-rate-limit'], [RateLimitsType.TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT, 'tenant-profile.tenant-notification-requests-per-rule-rate-limit'], + [RateLimitsType.EDGE_EVENTS_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-rate-limit'], + [RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-per-edge-rate-limit'], ] ); @@ -73,6 +77,8 @@ export const rateLimitsDialogTitleTranslationMap = new Map
- + -
+
diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts index e65658ea53..2cdef0dea4 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts @@ -34,6 +34,7 @@ import { selectHasRepository } from '@core/auth/auth.selectors'; import { catchError, mergeMap, take } from 'rxjs/operators'; import { of } from 'rxjs'; import { TbPopoverComponent } from '@shared/components/popover.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-repository-settings', @@ -48,6 +49,10 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit @Input() popoverComponent: TbPopoverComponent; + @Input() + @coerceBoolean() + hideLoadingBar = false; + repositorySettingsForm: UntypedFormGroup; settings: RepositorySettings = null; diff --git a/ui-ngx/src/app/modules/home/components/vc/version-control.component.html b/ui-ngx/src/app/modules/home/components/vc/version-control.component.html index 4495ce5c5e..eb4b1d962b 100644 --- a/ui-ngx/src/app/modules/home/components/vc/version-control.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/version-control.component.html @@ -15,9 +15,11 @@ limitations under the License. --> - + *ngIf="!(hasRepository$ | async); else versionsTable"> Array; @@ -48,32 +43,13 @@ export interface WidgetActionDescriptorInfo extends WidgetActionDescriptor { typeName?: string; } -export function toWidgetActionDescriptor(action: WidgetActionDescriptorInfo): WidgetActionDescriptor { +export const toWidgetActionDescriptor = (action: WidgetActionDescriptorInfo): WidgetActionDescriptor => { const copy = deepClone(action); delete copy.actionSourceId; delete copy.actionSourceName; delete copy.typeName; return copy; -} - -export function toCustomAction(action: WidgetActionDescriptorInfo): CustomActionDescriptor { - let result: CustomActionDescriptor; - if (!action || (isUndefined(action.customFunction) && isUndefined(action.customHtml) && isUndefined(action.customCss))) { - result = { - customHtml: customSampleHtml, - customCss: customSampleCss, - customFunction: customSampleJs - }; - } else { - result = { - customHtml: action.customHtml, - customCss: action.customCss, - customFunction: action.customFunction - }; - } - result.customResources = action && isDefined(action.customResources) ? deepClone(action.customResources) : []; - return result; -} +}; export class WidgetActionsDatasource implements DataSource { diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html index 94e88350e1..f75b2e44e7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html @@ -27,219 +27,82 @@ -
+
- - widget-config.action-source - - - {{ actionSourceName(actionSourceItem.value) }} - - - - {{ 'widget-config.action-source-required' | translate }} - - - - widget-config.action-name - - - - {{ 'widget-config.action-name-required' | translate }} - - - - - - - - {{ 'widget-config.show-hide-action-using-function' | translate }} - - - - widget-config.action-type - - - {{ widgetActionTypeTranslations.get(widgetActionType[actionType]) | translate }} - - - - {{ 'widget-config.action-type-required' | translate }} - - -
- -
widget-action.target-dashboard
- -
- - - - - - - - - - - {{ 'widget-action.target-dashboard-state-required' | translate }} - - - - - - {{ 'widget-action.open-right-layout' | translate }} - - - - - {{ 'widget-action.open-new-browser-tab' | translate }} - - - - - {{ 'widget-action.set-entity-from-widget' | translate }} - - - alias.state-entity-parameter-name - - - - - - widget-action.state-display-type - - - {{ stateDisplayTypeName(displayType) }} +
+
+
{{'widget-config.action-source' | translate}}*
+ + + + {{ actionSourceName(actionSourceItem.value) }} + + warning + -
- - - widget-action.dialog-title - - - - {{ 'widget-action.dialog-hide-dashboard-toolbar' | translate }} - - - widget-action.dialog-width - - - {{ 'widget-action.dialog-size-range-error' | translate }} - - - {{ 'widget-action.dialog-size-range-error' | translate }} - - - - widget-action.dialog-height - - - {{ 'widget-action.dialog-size-range-error' | translate }} - - - {{ 'widget-action.dialog-size-range-error' | translate }} - - - - - - widget-action.popover-preferred-placement - - - {{ popoverPlacementName(placement) }} - - - - - {{ 'widget-action.popover-hide-on-click-outside' | translate }} - - - {{ 'widget-action.popover-hide-dashboard-toolbar' | translate }} - - - widget-action.popover-width - - - - widget-action.popover-height - - - - - +
+
+
{{'widget-config.action-name' | translate}}*
+ + + + warning + + +
+
+
{{'widget-config.icon' | translate}}
+ + +
+
+ + + + + {{ 'widget-config.show-hide-action-using-function' | translate }} + + + + + -
- - - - - - - - - - - - - + +
+ + +
@@ -251,8 +114,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts index 15ece4f772..b10085c294 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.ts @@ -14,46 +14,37 @@ /// limitations under the License. /// -import { Component, ElementRef, Inject, OnInit, SkipSelf, ViewChild } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit, SkipSelf } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { + FormGroupDirective, + NgForm, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, - FormGroupDirective, - NgForm, ValidatorFn, Validators } from '@angular/forms'; -import { Observable, of, Subject, Subscription } from 'rxjs'; +import { Subject } from 'rxjs'; import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; import { - toCustomAction, WidgetActionCallbacks, WidgetActionDescriptorInfo, WidgetActionsData } from '@home/components/widget/action/manage-widget-actions.component.models'; import { UtilsService } from '@core/services/utils.service'; import { + actionDescriptorToAction, defaultWidgetAction, WidgetActionSource, - WidgetActionType, - widgetActionTypeTranslationMap + widgetType } from '@shared/models/widget.models'; -import { map, mergeMap, startWith, takeUntil, tap } from 'rxjs/operators'; -import { DashboardService } from '@core/http/dashboard.service'; -import { Dashboard } from '@shared/models/dashboard.models'; -import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; -import { CustomActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; -import { isDefinedAndNotNull } from '@core/utils'; -import { MobileActionEditorComponent } from '@home/components/widget/action/mobile-action-editor.component'; -import { widgetType } from '@shared/models/widget.models'; +import { takeUntil } from 'rxjs/operators'; +import { CustomActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { WidgetService } from '@core/http/widget.service'; -import { TranslateService } from '@ngx-translate/core'; -import { PopoverPlacement, PopoverPlacements } from '@shared/components/popover.models'; export interface WidgetActionDialogData { isAdd: boolean; @@ -63,18 +54,6 @@ export interface WidgetActionDialogData { widgetType: widgetType; } -const stateDisplayTypes = ['normal', 'separateDialog', 'popover'] as const; -type stateDisplayTypeTuple = typeof stateDisplayTypes; -export type stateDisplayType = stateDisplayTypeTuple[number]; - -const stateDisplayTypesTranslations = new Map( - [ - ['normal', 'widget-action.open-normal'], - ['separateDialog', 'widget-action.open-in-separate-dialog'], - ['popover', 'widget-action.open-in-popover'], - ] -); - @Component({ selector: 'tb-widget-action-dialog', templateUrl: './widget-action-dialog.component.html', @@ -82,49 +61,25 @@ const stateDisplayTypesTranslations = new Map( styleUrls: [] }) export class WidgetActionDialogComponent extends DialogComponent implements OnInit, ErrorStateMatcher { - - @ViewChild('dashboardStateInput') dashboardStateInput: ElementRef; - - @ViewChild('mobileActionEditor', {static: false}) mobileActionEditor: MobileActionEditorComponent; + WidgetActionDescriptorInfo> implements OnInit, OnDestroy, ErrorStateMatcher { private destroy$ = new Subject(); - private dashboard: Dashboard; - widgetActionFormGroup: UntypedFormGroup; - actionTypeFormGroup: UntypedFormGroup; - actionTypeFormGroupSubscriptions: Subscription[] = []; - stateDisplayTypeFormGroup: UntypedFormGroup; isAdd: boolean; action: WidgetActionDescriptorInfo; - widgetActionTypes = Object.keys(WidgetActionType); - widgetActionTypeTranslations = widgetActionTypeTranslationMap; - widgetActionType = WidgetActionType; - - filteredDashboardStates: Observable>; - targetDashboardStateSearchText = ''; - selectedDashboardStateIds: Observable>; - customActionEditorCompleter = CustomActionEditorCompleter; submitted = false; - widgetType = widgetType; functionScopeVariables: string[]; - allStateDisplayTypes = stateDisplayTypes; - allPopoverPlacements = PopoverPlacements; - constructor(protected store: Store, protected router: Router, private utils: UtilsService, - private dashboardService: DashboardService, - private dashboardUtils: DashboardUtilsService, private widgetService: WidgetService, - private translate: TranslateService, @Inject(MAT_DIALOG_DATA) public data: WidgetActionDialogData, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, @@ -136,7 +91,7 @@ export class WidgetActionDialogComponent extends DialogComponent { - this.updateActionTypeFormGroup(type); - }); this.widgetActionFormGroup.get('actionSourceId').valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => { @@ -209,233 +158,6 @@ export class WidgetActionDialogComponent extends DialogComponent s.unsubscribe()); - this.actionTypeFormGroupSubscriptions.length = 0; - this.actionTypeFormGroup = this.fb.group({}); - if (type) { - switch (type) { - case WidgetActionType.openDashboard: - case WidgetActionType.openDashboardState: - case WidgetActionType.updateDashboardState: - this.actionTypeFormGroup.addControl( - 'targetDashboardStateId', - this.fb.control(action ? action.targetDashboardStateId : null, - type === WidgetActionType.openDashboardState ? [Validators.required] : []) - ); - this.actionTypeFormGroup.addControl( - 'setEntityId', - this.fb.control(this.data.widgetType === widgetType.static ? false : action ? action.setEntityId : true, []) - ); - this.actionTypeFormGroup.addControl( - 'stateEntityParamName', - this.fb.control(action ? action.stateEntityParamName : null, []) - ); - if (type === WidgetActionType.openDashboard) { - this.actionTypeFormGroup.addControl( - 'openNewBrowserTab', - this.fb.control(action ? action.openNewBrowserTab : false, []) - ); - this.actionTypeFormGroup.addControl( - 'targetDashboardId', - this.fb.control(action ? action.targetDashboardId : null, - [Validators.required]) - ); - this.setupSelectedDashboardStateIds(); - } else { - if (type === WidgetActionType.openDashboardState) { - const displayType = this.getStateDisplayType(action); - this.actionTypeFormGroup.addControl( - 'stateDisplayType', - this.fb.control(this.getStateDisplayType(action), [Validators.required]) - ); - this.updateStateDisplayTypeFormGroup(displayType, action); - this.actionTypeFormGroupSubscriptions.push( - this.actionTypeFormGroup.get('stateDisplayType').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((displayTypeValue: stateDisplayType) => { - this.updateStateDisplayTypeFormGroup(displayTypeValue); - }) - ); - } - this.actionTypeFormGroup.addControl( - 'openRightLayout', - this.fb.control(action ? action.openRightLayout : false, []) - ); - } - this.setupFilteredDashboardStates(); - break; - case WidgetActionType.custom: - this.actionTypeFormGroup.addControl( - 'customFunction', - this.fb.control(action ? action.customFunction : null, []) - ); - break; - case WidgetActionType.customPretty: - this.actionTypeFormGroup.addControl( - 'customAction', - this.fb.control(toCustomAction(action), [Validators.required]) - ); - break; - case WidgetActionType.mobileAction: - this.actionTypeFormGroup.addControl( - 'mobileAction', - this.fb.control(action ? action.mobileAction : null, [Validators.required]) - ); - break; - } - } - } - - private updateStateDisplayTypeFormGroup(displayType?: stateDisplayType, action?: WidgetActionDescriptorInfo) { - this.stateDisplayTypeFormGroup = this.fb.group({}); - if (displayType) { - switch (displayType) { - case 'normal': - break; - case 'separateDialog': - this.stateDisplayTypeFormGroup.addControl( - 'dialogTitle', - this.fb.control(action ? action.dialogTitle : '', []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'dialogHideDashboardToolbar', - this.fb.control(action && isDefinedAndNotNull(action.dialogHideDashboardToolbar) - ? action.dialogHideDashboardToolbar : true, []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'dialogWidth', - this.fb.control(action ? action.dialogWidth : null, [Validators.min(1), Validators.max(100)]) - ); - this.stateDisplayTypeFormGroup.addControl( - 'dialogHeight', - this.fb.control(action ? action.dialogHeight : null, [Validators.min(1), Validators.max(100)]) - ); - break; - case 'popover': - this.stateDisplayTypeFormGroup.addControl( - 'popoverPreferredPlacement', - this.fb.control(action && isDefinedAndNotNull(action.popoverPreferredPlacement) - ? action.popoverPreferredPlacement : 'top', []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'popoverHideOnClickOutside', - this.fb.control(action && isDefinedAndNotNull(action.popoverHideOnClickOutside) - ? action.popoverHideOnClickOutside : true, []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'popoverHideDashboardToolbar', - this.fb.control(action && isDefinedAndNotNull(action.popoverHideDashboardToolbar) - ? action.popoverHideDashboardToolbar : true, []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'popoverWidth', - this.fb.control(action && isDefinedAndNotNull(action.popoverWidth) ? action.popoverWidth : '25vw', []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'popoverHeight', - this.fb.control(action && isDefinedAndNotNull(action.popoverHeight) ? action.popoverHeight : '25vh', []) - ); - this.stateDisplayTypeFormGroup.addControl( - 'popoverStyle', - this.fb.control(action && isDefinedAndNotNull(action.popoverStyle) ? action.popoverStyle : {}, []) - ); - break; - } - } - } - - private getStateDisplayType(action?: WidgetActionDescriptorInfo): stateDisplayType { - let res: stateDisplayType = 'normal'; - if (action) { - if (action.openInSeparateDialog) { - res = 'separateDialog'; - } else if (action.openInPopover) { - res = 'popover'; - } - } - return res; - } - - private setupSelectedDashboardStateIds() { - this.selectedDashboardStateIds = - this.actionTypeFormGroup.get('targetDashboardId').valueChanges.pipe( - tap((dashboardId) => { - if (!dashboardId) { - this.actionTypeFormGroup.get('targetDashboardStateId') - .patchValue('', {emitEvent: true}); - } - - this.targetDashboardStateSearchText = ''; - }), - mergeMap((dashboardId) => { - if (dashboardId) { - if (this.dashboard?.id.id === dashboardId) { - return of(this.dashboard); - } else { - return this.dashboardService.getDashboard(dashboardId); - } - } else { - return of(null); - } - }), - map((dashboard: Dashboard) => { - if (dashboard) { - if (this.dashboard?.id.id !== dashboard.id.id) { - this.dashboard = this.dashboardUtils.validateAndUpdateDashboard(dashboard); - } - - return Object.keys(this.dashboard.configuration.states); - } else { - return []; - } - }) - ); - } - - private setupFilteredDashboardStates() { - this.targetDashboardStateSearchText = ''; - this.filteredDashboardStates = this.actionTypeFormGroup.get('targetDashboardStateId').valueChanges - .pipe( - startWith(''), - map(value => value ? value : ''), - mergeMap(name => this.fetchDashboardStates(name)), - takeUntil(this.destroy$) - ); - } - - private fetchDashboardStates(searchText?: string): Observable> { - this.targetDashboardStateSearchText = searchText; - if (this.widgetActionFormGroup.get('type').value === WidgetActionType.openDashboard) { - return this.selectedDashboardStateIds.pipe( - map(stateIds => { - const result = searchText ? stateIds.filter(this.createFilterForDashboardState(searchText)) : stateIds; - if (result && result.length) { - return result; - } else { - return [searchText]; - } - }) - ); - } else { - return of(this.data.callbacks.fetchDashboardStates(searchText)); - } - } - - private createFilterForDashboardState(query: string): (stateId: string) => boolean { - const lowercaseQuery = query.toLowerCase(); - return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0; - } - - public clearTargetDashboardState(value: string = '') { - this.dashboardStateInput.nativeElement.value = value; - this.actionTypeFormGroup.get('targetDashboardStateId').patchValue(value, {emitEvent: true}); - setTimeout(() => { - this.dashboardStateInput.nativeElement.blur(); - this.dashboardStateInput.nativeElement.focus(); - }, 0); - } - private validateActionName(): ValidatorFn { return (c: UntypedFormControl) => { const newName = c.value; @@ -474,53 +196,16 @@ export class WidgetActionDialogComponent extends DialogComponent + + + +
+
widgets.action-button.behavior
+
+
widgets.action-button.on-click
+ + +
+
+
widgets.button-state.activated-state
+ +
+
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + +
+
+
widget-config.card-appearance
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/action-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/action-button-basic-config.component.ts new file mode 100644 index 0000000000..4485966ac7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/action-button-basic-config.component.ts @@ -0,0 +1,139 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + actionDescriptorToAction, + Datasource, + defaultWidgetAction, + TargetDevice, + WidgetAction, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { guid } from '@core/utils'; +import { ValueType } from '@shared/models/constants'; +import { getTargetDeviceFromDatasources } from '@shared/models/widget-settings.models'; +import { + actionButtonDefaultSettings, + ActionButtonWidgetSettings +} from '@home/components/widget/lib/button/action-button-widget.models'; + +@Component({ + selector: 'tb-action-button-basic-config', + templateUrl: './action-button-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class ActionButtonBasicConfigComponent extends BasicWidgetConfigComponent { + + get targetDevice(): TargetDevice { + const datasources: Datasource[] = this.actionButtonWidgetConfigForm.get('datasources').value; + return getTargetDeviceFromDatasources(datasources); + } + + valueType = ValueType; + + actionButtonWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.actionButtonWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: ActionButtonWidgetSettings = {...actionButtonDefaultSettings, ...(configData.config.settings || {})}; + const onClickAction = this.getOnClickAction(configData.config); + this.actionButtonWidgetConfigForm = this.fb.group({ + datasources: [configData.config.datasources, []], + + onClickAction: [onClickAction, []], + activatedState: [settings.activatedState, []], + disabledState: [settings.disabledState, []], + + appearance: [settings.appearance, []], + + borderRadius: [configData.config.borderRadius, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + + this.widgetConfig.config.datasources = config.datasources; + this.setOnClickAction(this.widgetConfig.config, config.onClickAction); + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.activatedState = config.activatedState; + this.widgetConfig.config.settings.disabledState = config.disabledState; + + this.widgetConfig.config.settings.appearance = config.appearance; + + this.widgetConfig.config.borderRadius = config.borderRadius; + + return this.widgetConfig; + } + + private getOnClickAction(config: WidgetConfig): WidgetAction { + let clickAction: WidgetAction; + const actions = config.actions; + if (actions && actions.click) { + const descriptors = actions.click; + if (descriptors?.length) { + const descriptor = descriptors[0]; + clickAction = actionDescriptorToAction(descriptor); + } + } + if (!clickAction) { + clickAction = defaultWidgetAction(); + } + return clickAction; + } + + private setOnClickAction(config: WidgetConfig, clickAction: WidgetAction): void { + let actions = config.actions; + if (!actions) { + actions = {}; + config.actions = actions; + } + let descriptors = actions.click; + if (!descriptors) { + descriptors = []; + actions.click = descriptors; + } + let descriptor = descriptors[0]; + if (!descriptor) { + descriptor = { + id: guid(), + name: 'onClick', + icon: 'more_horiz', + ...clickAction + }; + descriptors[0] = descriptor; + } else { + descriptors[0] = {...descriptor, ...clickAction}; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.html new file mode 100644 index 0000000000..4523ad0efd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.html @@ -0,0 +1,59 @@ + + + +
+
widgets.command-button.behavior
+
+
widgets.command-button.on-click
+ +
+
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + +
+
+
widget-config.card-appearance
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.ts new file mode 100644 index 0000000000..2d7d7fc1cc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.ts @@ -0,0 +1,85 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { TargetDevice, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { ValueType } from '@shared/models/constants'; +import { + commandButtonDefaultSettings, + CommandButtonWidgetSettings +} from '@home/components/widget/lib/button/command-button-widget.models'; + +@Component({ + selector: 'tb-command-button-basic-config', + templateUrl: './command-button-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class CommandButtonBasicConfigComponent extends BasicWidgetConfigComponent { + + get targetDevice(): TargetDevice { + return this.commandButtonWidgetConfigForm.get('targetDevice').value; + } + + valueType = ValueType; + + commandButtonWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.commandButtonWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: CommandButtonWidgetSettings = {...commandButtonDefaultSettings, ...(configData.config.settings || {})}; + this.commandButtonWidgetConfigForm = this.fb.group({ + targetDevice: [configData.config.targetDevice, []], + + onClickState: [settings.onClickState, []], + disabledState: [settings.disabledState, []], + + appearance: [settings.appearance, []], + + borderRadius: [configData.config.borderRadius, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + + this.widgetConfig.config.targetDevice = config.targetDevice; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.onClickState = config.onClickState; + this.widgetConfig.config.settings.disabledState = config.disabledState; + + this.widgetConfig.config.settings.appearance = config.appearance; + + this.widgetConfig.config.borderRadius = config.borderRadius; + + return this.widgetConfig; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html new file mode 100644 index 0000000000..8d4dcf404f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html @@ -0,0 +1,197 @@ + + + +
+
widgets.power-button.behavior
+
+
widgets.rpc-state.initial-state
+ +
+
+
widgets.power-button.power-on
+ +
+
+
widgets.power-button.power-off
+ +
+
+
widgets.rpc-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + + {{ powerButtonLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + + + + + +
+
+
+
{{ 'widgets.power-button.power-on-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
{{ 'widgets.power-button.power-off-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
{{ 'widgets.power-button.disabled-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.ts new file mode 100644 index 0000000000..174ded9ed3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.ts @@ -0,0 +1,195 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { TargetDevice, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { isUndefined } from '@core/utils'; +import { ValueType } from '@shared/models/constants'; +import { + powerButtonDefaultSettings, + powerButtonLayoutImages, + powerButtonLayouts, + powerButtonLayoutTranslations, + PowerButtonWidgetSettings +} from '@home/components/widget/lib/rpc/power-button-widget.models'; +import { cssSizeToStrSize, resolveCssSize } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-power-button-basic-config', + templateUrl: './power-button-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class PowerButtonBasicConfigComponent extends BasicWidgetConfigComponent { + + get targetDevice(): TargetDevice { + return this.powerButtonWidgetConfigForm.get('targetDevice').value; + } + + powerButtonLayouts = powerButtonLayouts; + + powerButtonLayoutTranslationMap = powerButtonLayoutTranslations; + powerButtonLayoutImageMap = powerButtonLayoutImages; + + valueType = ValueType; + + powerButtonWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.powerButtonWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: PowerButtonWidgetSettings = {...powerButtonDefaultSettings, ...(configData.config.settings || {})}; + const iconSize = resolveCssSize(configData.config.iconSize); + this.powerButtonWidgetConfigForm = this.fb.group({ + targetDevice: [configData.config.targetDevice, []], + + initialState: [settings.initialState, []], + onUpdateState: [settings.onUpdateState, []], + offUpdateState: [settings.offUpdateState, []], + disabledState: [settings.disabledState, []], + + layout: [settings.layout, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + mainColorOn: [settings.mainColorOn, []], + backgroundColorOn: [settings.backgroundColorOn, []], + + mainColorOff: [settings.mainColorOff, []], + backgroundColorOff: [settings.backgroundColorOff, []], + + mainColorDisabled: [settings.mainColorDisabled, []], + backgroundColorDisabled: [settings.backgroundColorDisabled, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + this.widgetConfig.config.targetDevice = config.targetDevice; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.initialState = config.initialState; + this.widgetConfig.config.settings.onUpdateState = config.onUpdateState; + this.widgetConfig.config.settings.offUpdateState = config.offUpdateState; + this.widgetConfig.config.settings.disabledState = config.disabledState; + + this.widgetConfig.config.settings.layout = config.layout; + + this.widgetConfig.config.settings.mainColorOn = config.mainColorOn; + this.widgetConfig.config.settings.backgroundColorOn = config.backgroundColorOn; + + this.widgetConfig.config.settings.mainColorOff = config.mainColorOff; + this.widgetConfig.config.settings.backgroundColorOff = config.backgroundColorOff; + + this.widgetConfig.config.settings.mainColorDisabled = config.mainColorDisabled; + this.widgetConfig.config.settings.backgroundColorDisabled = config.backgroundColorDisabled; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.powerButtonWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.powerButtonWidgetConfigForm.get('showIcon').value; + if (showTitle) { + this.powerButtonWidgetConfigForm.get('title').enable(); + this.powerButtonWidgetConfigForm.get('titleFont').enable(); + this.powerButtonWidgetConfigForm.get('titleColor').enable(); + this.powerButtonWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.powerButtonWidgetConfigForm.get('iconSize').enable(); + this.powerButtonWidgetConfigForm.get('iconSizeUnit').enable(); + this.powerButtonWidgetConfigForm.get('icon').enable(); + this.powerButtonWidgetConfigForm.get('iconColor').enable(); + } else { + this.powerButtonWidgetConfigForm.get('iconSize').disable(); + this.powerButtonWidgetConfigForm.get('iconSizeUnit').disable(); + this.powerButtonWidgetConfigForm.get('icon').disable(); + this.powerButtonWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.powerButtonWidgetConfigForm.get('title').disable(); + this.powerButtonWidgetConfigForm.get('titleFont').disable(); + this.powerButtonWidgetConfigForm.get('titleColor').disable(); + this.powerButtonWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.powerButtonWidgetConfigForm.get('iconSize').disable(); + this.powerButtonWidgetConfigForm.get('iconSizeUnit').disable(); + this.powerButtonWidgetConfigForm.get('icon').disable(); + this.powerButtonWidgetConfigForm.get('iconColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html index 249acae52a..9f8d580bbc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html @@ -86,7 +86,7 @@
-
+
{{ 'widgets.progress-bar.value' | translate }} @@ -104,7 +104,7 @@
-
+
{{ 'widgets.progress-bar.range' | translate }}
widgets.progress-bar.min
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html index d79947185b..b00a89969b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html @@ -34,7 +34,7 @@ {{ 'widgets.liquid-level-card.title' | translate }} -
+
@@ -49,7 +49,7 @@ {{ 'widgets.liquid-level-card.icon' | translate }} -
+
@@ -64,7 +64,7 @@
-
+
widgets.liquid-level-card.shape
@@ -96,7 +96,7 @@ formControlName="shapeAttributeName">
-
+
widgets.liquid-level-card.shape-by-attribute
widgets.liquid-level-card.units
widgets.liquid-level-card.datasource-units
-
+
-
+
widgets.liquid-level-card.widget-units
-
+
@@ -154,9 +154,9 @@
-
+
widgets.liquid-level-card.total-volume
-
+
@@ -184,10 +184,30 @@ formControlName="volumeAttributeName"> - +
+
+
widgets.liquid-level-card.total-volume-units
+
+ + + + {{ DataSourceTypeTranslations.get(type) | translate }} + + + + + + + +
@@ -206,7 +226,7 @@
widgets.liquid-level-card.value
-
+
@@ -221,7 +241,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -247,7 +267,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -268,7 +288,7 @@ {{ 'widgets.value-card.date' | translate }} -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index b246ed2db1..0fc034d496 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -194,7 +194,9 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon volumeSource: [settings.volumeSource, []], volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]], volumeAttributeName: [settings.volumeAttributeName, [Validators.required]], + volumeUnitsSource: [settings.volumeUnitsSource, []], volumeUnits: [settings.volumeUnits, [Validators.required]], + volumeUnitsAttributeName: [settings.volumeUnitsAttributeName, [Validators.required]], volumeFont: [settings.volumeFont, []], volumeColor: [settings.volumeColor, []], units: [settings.units, [Validators.required]], @@ -260,6 +262,8 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon this.widgetConfig.config.settings.volumeSource = config.volumeSource; this.widgetConfig.config.settings.volumeConstant = config.volumeConstant; this.widgetConfig.config.settings.volumeAttributeName = config.volumeAttributeName; + this.widgetConfig.config.settings.volumeUnitsSource = config.volumeUnitsSource; + this.widgetConfig.config.settings.volumeUnitsAttributeName = config.volumeUnitsAttributeName; this.widgetConfig.config.settings.volumeUnits = config.volumeUnits; this.widgetConfig.config.settings.volumeFont = config.volumeFont; this.widgetConfig.config.settings.volumeColor = config.volumeColor; @@ -294,7 +298,7 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon protected validatorTriggers(): string[] { return [ 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', 'showTitleIcon', 'volumeSource', - 'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource' + 'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource', 'volumeUnitsSource' ]; } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.html index 46bafec9dc..c6ac3abb45 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.html @@ -21,29 +21,45 @@
widgets.single-switch.behavior
widgets.rpc-state.initial-state
- +
widgets.rpc-state.turn-on
- + [widgetType]="widgetType" + formControlName="onUpdateState">
widgets.rpc-state.turn-off
- + [widgetType]="widgetType" + formControlName="offUpdateState"> +
+
+
widgets.rpc-state.disabled-state
+
@@ -66,7 +82,7 @@ {{ 'widgets.single-switch.auto-scale' | translate }}
-
+
{{ 'widgets.single-switch.label' | translate }} @@ -83,7 +99,7 @@
-
+
{{ 'widgets.single-switch.icon' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts index ed95847654..75c93bc852 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/single-switch-basic-config.component.ts @@ -37,7 +37,7 @@ import { ValueType } from '@shared/models/constants'; templateUrl: './single-switch-basic-config.component.html', styleUrls: ['../basic-config.scss'] }) -export class SingSwitchBasicConfigComponent extends BasicWidgetConfigComponent { +export class SingleSwitchBasicConfigComponent extends BasicWidgetConfigComponent { get targetDevice(): TargetDevice { return this.singleSwitchWidgetConfigForm.get('targetDevice').value; @@ -70,6 +70,7 @@ export class SingSwitchBasicConfigComponent extends BasicWidgetConfigComponent { initialState: [settings.initialState, []], onUpdateState: [settings.onUpdateState, []], offUpdateState: [settings.offUpdateState, []], + disabledState: [settings.disabledState, []], layout: [settings.layout, []], autoScale: [settings.autoScale, []], @@ -120,6 +121,7 @@ export class SingSwitchBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings.initialState = config.initialState; this.widgetConfig.config.settings.onUpdateState = config.onUpdateState; this.widgetConfig.config.settings.offUpdateState = config.offUpdateState; + this.widgetConfig.config.settings.disabledState = config.disabledState; this.widgetConfig.config.settings.layout = config.layout; this.widgetConfig.config.settings.autoScale = config.autoScale; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html new file mode 100644 index 0000000000..a8d84d4dc4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html @@ -0,0 +1,271 @@ + + + +
+
widgets.slider.behavior
+
+
widgets.slider.initial-value
+ +
+
+
widgets.slider.on-value-change
+ +
+
+
widgets.rpc-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + + {{ sliderLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.slider.auto-scale' | translate }} + +
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.slider.icon' | translate }} + +
+ + + + + + + + +
+
+
+ + {{ 'widgets.slider.value' | translate }} + +
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.slider.range' | translate }}
+
+
widgets.slider.min
+ + + +
widgets.slider.max
+ + + +
+
+
+ + {{ 'widgets.slider.range-ticks' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.slider.tick-marks' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.slider.colors' | translate }}
+
+
+
widgets.slider.main
+ + +
+ +
+
widgets.slider.background
+ + +
+
+
+
+
{{ 'widgets.rpc-state.disabled-state' | translate }}
+
+
+
widgets.slider.main
+ + +
+ +
+
widgets.slider.background
+ + +
+
+
+
+
+ {{ 'widgets.slider.left-icon' | translate }} +
+
+ + + + + + + + +
+
+
+
+ {{ 'widgets.slider.right-icon' | translate }} +
+
+ + + + + + + + +
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.ts new file mode 100644 index 0000000000..b80c6acb08 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.ts @@ -0,0 +1,309 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { TargetDevice, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { ValueType } from '@shared/models/constants'; +import { + SliderLayout, + sliderLayoutImages, + sliderLayouts, + sliderLayoutTranslations, + sliderWidgetDefaultSettings, + SliderWidgetSettings +} from '@home/components/widget/lib/rpc/slider-widget.models'; +import { cssSizeToStrSize, resolveCssSize } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-slider-basic-config', + templateUrl: './slider-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class SliderBasicConfigComponent extends BasicWidgetConfigComponent { + + get targetDevice(): TargetDevice { + return this.sliderWidgetConfigForm.get('targetDevice').value; + } + + sliderLayout = SliderLayout; + + sliderLayouts = sliderLayouts; + + sliderLayoutTranslationMap = sliderLayoutTranslations; + sliderLayoutImageMap = sliderLayoutImages; + + valueType = ValueType; + + sliderWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.sliderWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: SliderWidgetSettings = {...sliderWidgetDefaultSettings, ...(configData.config.settings || {})}; + const iconSize = resolveCssSize(configData.config.iconSize); + this.sliderWidgetConfigForm = this.fb.group({ + targetDevice: [configData.config.targetDevice, []], + + initialState: [settings.initialState, []], + valueChange: [settings.valueChange, []], + disabledState: [settings.disabledState, []], + + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + showValue: [settings.showValue, []], + valueUnits: [settings.valueUnits, []], + valueDecimals: [settings.valueDecimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + tickMin: [settings.tickMin, []], + tickMax: [settings.tickMax, []], + + showTicks: [settings.showTicks, []], + ticksFont: [settings.ticksFont, []], + ticksColor: [settings.ticksColor, []], + + showTickMarks: [settings.showTickMarks, []], + tickMarksCount: [settings.tickMarksCount, [Validators.min(2)]], + tickMarksColor: [settings.tickMarksColor, []], + + mainColor: [settings.mainColor, []], + backgroundColor: [settings.backgroundColor, []], + + mainColorDisabled: [settings.mainColorDisabled, []], + backgroundColorDisabled: [settings.backgroundColorDisabled, []], + + leftIconSize: [settings.leftIconSize, [Validators.min(0)]], + leftIconSizeUnit: [settings.leftIconSizeUnit, []], + leftIcon: [settings.leftIcon, []], + leftIconColor: [settings.leftIconColor, []], + + rightIconSize: [settings.rightIconSize, [Validators.min(0)]], + rightIconSizeUnit: [settings.rightIconSizeUnit, []], + rightIcon: [settings.rightIcon, []], + rightIconColor: [settings.rightIconColor, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + this.widgetConfig.config.targetDevice = config.targetDevice; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.initialState = config.initialState; + this.widgetConfig.config.settings.valueChange = config.valueChange; + this.widgetConfig.config.settings.disabledState = config.disabledState; + + this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; + + this.widgetConfig.config.settings.showValue = config.showValue; + this.widgetConfig.config.settings.valueUnits = config.valueUnits; + this.widgetConfig.config.settings.valueDecimals = config.valueDecimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueColor = config.valueColor; + + this.widgetConfig.config.settings.tickMin = config.tickMin; + this.widgetConfig.config.settings.tickMax = config.tickMax; + + this.widgetConfig.config.settings.showTicks = config.showTicks; + this.widgetConfig.config.settings.ticksFont = config.ticksFont; + this.widgetConfig.config.settings.ticksColor = config.ticksColor; + + this.widgetConfig.config.settings.showTickMarks = config.showTickMarks; + this.widgetConfig.config.settings.tickMarksCount = config.tickMarksCount; + this.widgetConfig.config.settings.tickMarksColor = config.tickMarksColor; + + this.widgetConfig.config.settings.mainColor = config.mainColor; + this.widgetConfig.config.settings.backgroundColor = config.backgroundColor; + + this.widgetConfig.config.settings.mainColorDisabled = config.mainColorDisabled; + this.widgetConfig.config.settings.backgroundColorDisabled = config.backgroundColorDisabled; + + this.widgetConfig.config.settings.leftIconSize = config.leftIconSize; + this.widgetConfig.config.settings.leftIconSizeUnit = config.leftIconSizeUnit; + this.widgetConfig.config.settings.leftIcon = config.leftIcon; + this.widgetConfig.config.settings.leftIconColor = config.leftIconColor; + + this.widgetConfig.config.settings.rightIconSize = config.rightIconSize; + this.widgetConfig.config.settings.rightIconSizeUnit = config.rightIconSizeUnit; + this.widgetConfig.config.settings.rightIcon = config.rightIcon; + this.widgetConfig.config.settings.rightIconColor = config.rightIconColor; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showValue', 'showTicks', 'showTickMarks', 'layout']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.sliderWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.sliderWidgetConfigForm.get('showIcon').value; + const showValue: boolean = this.sliderWidgetConfigForm.get('showValue').value; + const showTicks: boolean = this.sliderWidgetConfigForm.get('showTicks').value; + const showTickMarks: boolean = this.sliderWidgetConfigForm.get('showTickMarks').value; + const layout: SliderLayout = this.sliderWidgetConfigForm.get('layout').value; + + const valueEnabled = layout !== SliderLayout.simplified; + const leftRightIconsEnabled = layout === SliderLayout.extended; + + if (showTitle) { + this.sliderWidgetConfigForm.get('title').enable(); + this.sliderWidgetConfigForm.get('titleFont').enable(); + this.sliderWidgetConfigForm.get('titleColor').enable(); + this.sliderWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.sliderWidgetConfigForm.get('iconSize').enable(); + this.sliderWidgetConfigForm.get('iconSizeUnit').enable(); + this.sliderWidgetConfigForm.get('icon').enable(); + this.sliderWidgetConfigForm.get('iconColor').enable(); + } else { + this.sliderWidgetConfigForm.get('iconSize').disable(); + this.sliderWidgetConfigForm.get('iconSizeUnit').disable(); + this.sliderWidgetConfigForm.get('icon').disable(); + this.sliderWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.sliderWidgetConfigForm.get('title').disable(); + this.sliderWidgetConfigForm.get('titleFont').disable(); + this.sliderWidgetConfigForm.get('titleColor').disable(); + this.sliderWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.sliderWidgetConfigForm.get('iconSize').disable(); + this.sliderWidgetConfigForm.get('iconSizeUnit').disable(); + this.sliderWidgetConfigForm.get('icon').disable(); + this.sliderWidgetConfigForm.get('iconColor').disable(); + } + + if (valueEnabled && showValue) { + this.sliderWidgetConfigForm.get('valueUnits').enable(); + this.sliderWidgetConfigForm.get('valueDecimals').enable(); + this.sliderWidgetConfigForm.get('valueFont').enable(); + this.sliderWidgetConfigForm.get('valueColor').enable(); + } else { + this.sliderWidgetConfigForm.get('valueUnits').disable(); + this.sliderWidgetConfigForm.get('valueDecimals').disable(); + this.sliderWidgetConfigForm.get('valueFont').disable(); + this.sliderWidgetConfigForm.get('valueColor').disable(); + } + + if (showTicks) { + this.sliderWidgetConfigForm.get('ticksFont').enable(); + this.sliderWidgetConfigForm.get('ticksColor').enable(); + } else { + this.sliderWidgetConfigForm.get('ticksFont').disable(); + this.sliderWidgetConfigForm.get('ticksColor').disable(); + } + + if (showTickMarks) { + this.sliderWidgetConfigForm.get('tickMarksCount').enable(); + this.sliderWidgetConfigForm.get('tickMarksColor').enable(); + } else { + this.sliderWidgetConfigForm.get('tickMarksCount').disable(); + this.sliderWidgetConfigForm.get('tickMarksColor').disable(); + } + + if (leftRightIconsEnabled) { + this.sliderWidgetConfigForm.get('leftIconSize').enable(); + this.sliderWidgetConfigForm.get('leftIconSizeUnit').enable(); + this.sliderWidgetConfigForm.get('leftIcon').enable(); + this.sliderWidgetConfigForm.get('leftIconColor').enable(); + this.sliderWidgetConfigForm.get('rightIconSize').enable(); + this.sliderWidgetConfigForm.get('rightIconSizeUnit').enable(); + this.sliderWidgetConfigForm.get('rightIcon').enable(); + this.sliderWidgetConfigForm.get('rightIconColor').enable(); + } else { + this.sliderWidgetConfigForm.get('leftIconSize').disable(); + this.sliderWidgetConfigForm.get('leftIconSizeUnit').disable(); + this.sliderWidgetConfigForm.get('leftIcon').disable(); + this.sliderWidgetConfigForm.get('leftIconColor').disable(); + this.sliderWidgetConfigForm.get('rightIconSize').disable(); + this.sliderWidgetConfigForm.get('rightIconSizeUnit').disable(); + this.sliderWidgetConfigForm.get('rightIcon').disable(); + this.sliderWidgetConfigForm.get('rightIconColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.sliderWidgetConfigForm.get('valueUnits').value; + const decimals: number = this.sliderWidgetConfigForm.get('valueDecimals').value; + return formatValue(48, decimals, units, false); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts index 63647fc604..800c831b67 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts @@ -521,7 +521,7 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange this.popoverService.hidePopover(trigger); } else { const colorPickerPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, ColorPickerPanelComponent, 'left', true, null, + this.viewContainerRef, ColorPickerPanelComponent, ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, { color: key.color }, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index 7777c6fdf9..b72f9f0e5d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -42,14 +42,14 @@ style="height: 56px; margin-bottom: 22px;" formControlName="alarmFilterConfig"> diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts index 11a871de08..1dd3aa3507 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts @@ -95,6 +95,10 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida return this.widgetConfigComponent.modelValue?.typeParameters?.dataKeysOptional; } + public get datasourcesOptional(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.datasourcesOptional; + } + public get maxDataKeys(): number { return this.widgetConfigComponent.modelValue?.typeParameters?.maxDataKeys; } @@ -276,18 +280,20 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida } private updateValidators() { - const type: DatasourceType = this.datasourceFormGroup.get('type').value; - this.datasourceFormGroup.get('deviceId').setValidators( - type === DatasourceType.device ? [Validators.required] : [] - ); - this.datasourceFormGroup.get('entityAliasId').setValidators( - (type === DatasourceType.entity || type === DatasourceType.entityCount) ? [Validators.required] : [] - ); - const newDataKeysRequired = !this.isDataKeysOptional(type); - this.datasourceFormGroup.get('dataKeys').setValidators(newDataKeysRequired ? [Validators.required] : []); - this.datasourceFormGroup.get('deviceId').updateValueAndValidity({emitEvent: false}); - this.datasourceFormGroup.get('entityAliasId').updateValueAndValidity({emitEvent: false}); - this.datasourceFormGroup.get('dataKeys').updateValueAndValidity({emitEvent: false}); + if (!this.datasourcesOptional) { + const type: DatasourceType = this.datasourceFormGroup.get('type').value; + this.datasourceFormGroup.get('deviceId').setValidators( + type === DatasourceType.device ? [Validators.required] : [] + ); + this.datasourceFormGroup.get('entityAliasId').setValidators( + (type === DatasourceType.entity || type === DatasourceType.entityCount) ? [Validators.required] : [] + ); + const newDataKeysRequired = !this.isDataKeysOptional(type); + this.datasourceFormGroup.get('dataKeys').setValidators(newDataKeysRequired ? [Validators.required] : []); + this.datasourceFormGroup.get('deviceId').updateValueAndValidity({emitEvent: false}); + this.datasourceFormGroup.get('entityAliasId').updateValueAndValidity({emitEvent: false}); + this.datasourceFormGroup.get('dataKeys').updateValueAndValidity({emitEvent: false}); + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts index 4cb274c3ef..a83d58a25e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasources.component.ts @@ -30,7 +30,7 @@ import { import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { Datasource, - DatasourceType, + DatasourceType, datasourceValid, JsonSettingsSchema, WidgetConfigMode, widgetType @@ -317,6 +317,9 @@ export class DatasourcesComponent implements ControlValueAccessor, OnInit, Valid } private datasourcesUpdated(datasources: Datasource[]) { + if (this.datasourcesOptional) { + datasources = datasources ? datasources.filter(d => datasourceValid(d)) : []; + } this.propagateChange(datasources); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index 1132e8bf91..441983a2e3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -23,7 +23,7 @@ import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; -import { DataKey, DatasourceType, KeyInfo, WidgetConfigMode } from '@shared/models/widget.models'; +import { DataKey, DatasourceType, KeyInfo, WidgetConfigMode, widgetType } from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { isDefinedAndNotNull } from '@core/utils'; @@ -63,6 +63,18 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement return this.widgetConfigComponent.aliasController; } + get callbacks(): WidgetConfigCallbacks { + return this.widgetConfigComponent.widgetConfigCallbacks; + } + + get widgetType(): widgetType { + return this.widgetConfigComponent.widgetType; + } + + get widgetEditMode(): boolean { + return this.widgetConfigComponent.widgetEditMode; + } + widgetConfigChangedEmitter = new EventEmitter(); widgetConfigChanged = this.widgetConfigChangedEmitter.asObservable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts new file mode 100644 index 0000000000..cac6928bac --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts @@ -0,0 +1,628 @@ +/// +/// Copyright © 2016-2024 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 { + AttributeData, + AttributeScope, + LatestTelemetry, + TelemetrySubscriber, + TelemetryType, + telemetryTypeTranslationsShort +} from '@shared/models/telemetry/telemetry.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { BehaviorSubject, forkJoin, Observable, Observer, of, throwError } from 'rxjs'; +import { catchError, delay, map, share, take } from 'rxjs/operators'; +import { UtilsService } from '@core/services/utils.service'; +import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; +import { + DataToValueSettings, + DataToValueType, + GetAttributeValueSettings, + GetValueAction, + GetValueSettings, + RpcSettings, + SetAttributeValueSettings, + SetValueAction, + SetValueSettings, + TelemetryValueSettings, + ValueActionSettings, + ValueToDataSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { ValueType } from '@shared/models/constants'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityId } from '@shared/models/id/entity-id'; + +@Directive() +// eslint-disable-next-line @angular-eslint/directive-class-suffix +export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + private loadingSubject = new BehaviorSubject(false); + private valueGetters: ValueGetter[] = []; + private valueActions: ValueAction[] = []; + + loading$ = this.loadingSubject.asObservable().pipe(share()); + + protected constructor(protected cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.actionWidget = this; + } + + ngAfterViewInit(): void { + const getValueObservables: Array> = []; + this.valueGetters.forEach(valueGetter => { + getValueObservables.push(valueGetter.getValue()); + }); + this.loadingSubject.next(true); + forkJoin(getValueObservables).subscribe( + { + next: () => { + this.loadingSubject.next(false); + }, + error: () => { + this.loadingSubject.next(false); + } + } + ); + } + + ngOnDestroy() { + this.valueActions.forEach(v => v.destroy()); + this.loadingSubject.complete(); + this.loadingSubject.unsubscribe(); + } + + public onInit() { + } + + public clearError() { + this.ctx.hideToast(this.ctx.toastTargetId); + } + + protected createValueGetter(getValueSettings: GetValueSettings, + valueType: ValueType, + valueObserver?: Partial>): ValueGetter { + const observer: Partial> = { + next: (value: V) => { + if (valueObserver?.next) { + valueObserver.next(value); + } + }, + error: (err: any) => { + const message = parseError(this.ctx, err); + this.onError(message); + if (valueObserver?.error) { + valueObserver.error(err); + } + } + }; + const valueGetter = ValueGetter.fromSettings(this.ctx, getValueSettings, valueType, observer); + this.valueGetters.push(valueGetter); + this.valueActions.push(valueGetter); + return valueGetter; + } + + protected createValueSetter(setValueSettings: SetValueSettings): ValueSetter { + const valueSetter = ValueSetter.fromSettings(this.ctx, setValueSettings); + this.valueActions.push(valueSetter); + return valueSetter; + } + + private onError(error: string) { + this.ctx.showErrorToast(error, 'bottom', 'center', this.ctx.toastTargetId, true); + } + + protected updateValue(valueSetter: ValueSetter, + value: V, + setValueObserver?: Partial>): void { + this.clearError(); + this.loadingSubject.next(true); + valueSetter.setValue(value).subscribe({ + next: () => { + if (setValueObserver?.next) { + setValueObserver.next(); + } + this.loadingSubject.next(false); + }, + error: (err) => { + this.loadingSubject.next(false); + if (setValueObserver?.error) { + setValueObserver.error(err); + } + const message = parseError(this.ctx, err); + this.onError(message); + } + }); + } +} + +type DataToValueFunction = (data: any) => V; + +export class DataToValueConverter { + + private readonly dataToValueFunction: DataToValueFunction; + private readonly compareToValue: any; + + constructor(private settings: DataToValueSettings, + private valueType: ValueType) { + this.compareToValue = settings.compareToValue; + switch (settings.type) { + case DataToValueType.FUNCTION: + try { + this.dataToValueFunction = new Function('data', settings.dataToValueFunction) as DataToValueFunction; + } catch (e) { + this.dataToValueFunction = (data) => data; + } + break; + case DataToValueType.NONE: + break; + } + } + + dataToValue(data: any): V { + let result: V; + switch (this.settings.type) { + case DataToValueType.FUNCTION: + result = data; + try { + result = this.dataToValueFunction(!!data ? JSON.parse(data) : data); + } catch (e) {} + break; + case DataToValueType.NONE: + result = data; + break; + } + if (this.valueType === ValueType.BOOLEAN) { + result = (result === this.compareToValue) as any; + } + return result; + } +} + +export abstract class ValueAction { + + protected constructor(protected ctx: WidgetContext, + protected settings: ValueActionSettings) {} + + protected handleError(err: any): Error { + const reason = parseError(this.ctx, err); + let errorMessage = this.ctx.translate.instant('widgets.value-action.error.failed-to-perform-action', + {actionLabel: this.settings.actionLabel}); + if (reason) { + errorMessage += '
' + reason; + } + return new Error(errorMessage); + } + + destroy(): void {} +} + +export abstract class ValueGetter extends ValueAction { + + static fromSettings(ctx: WidgetContext, + settings: GetValueSettings, + valueType: ValueType, + valueObserver: Partial>): ValueGetter { + switch (settings.action) { + case GetValueAction.DO_NOTHING: + return new DefaultValueGetter(ctx, settings, valueType, valueObserver); + case GetValueAction.EXECUTE_RPC: + return new ExecuteRpcValueGetter(ctx, settings, valueType, valueObserver); + case GetValueAction.GET_ATTRIBUTE: + return new AttributeValueGetter(ctx, settings, valueType, valueObserver); + case GetValueAction.GET_TIME_SERIES: + return new TimeSeriesValueGetter(ctx, settings, valueType, valueObserver); + } + } + + private readonly isSimulated: boolean; + private readonly dataConverter: DataToValueConverter; + + protected constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings); + this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; + if (this.settings.action !== GetValueAction.DO_NOTHING) { + this.dataConverter = new DataToValueConverter(settings.dataToValue, valueType); + } + } + + getValue(): Observable { + const valueObservable: Observable = (this.isSimulated ? of(null).pipe(delay(500)) : this.doGetValue()).pipe( + map((data) => { + if (this.dataConverter) { + return this.dataConverter.dataToValue(data); + } else { + return data; + } + }), + catchError(err => { + throw this.handleError(err); + }) + ); + valueObservable.subscribe({ + next: (value) => { + this.valueObserver.next(value); + }, + error: (err) => { + this.valueObserver.error(err); + } + }); + return valueObservable.pipe( + take(1) + ); + } + + destroy() { + super.destroy(); + } + + protected abstract doGetValue(): Observable; +} + +type ValueToDataFunction = (value: V) => any; + +export class ValueToDataConverter { + + private readonly constantValue: any; + private readonly valueToDataFunction: ValueToDataFunction; + + constructor(protected settings: ValueToDataSettings) { + switch (settings.type) { + case ValueToDataType.VALUE: + break; + case ValueToDataType.CONSTANT: + this.constantValue = this.settings.constantValue; + break; + case ValueToDataType.FUNCTION: + try { + this.valueToDataFunction = new Function('value', settings.valueToDataFunction) as ValueToDataFunction; + } catch (e) { + this.valueToDataFunction = (data) => data; + } + break; + case ValueToDataType.NONE: + break; + } + } + + valueToData(value: V): any { + switch (this.settings.type) { + case ValueToDataType.VALUE: + return value; + case ValueToDataType.CONSTANT: + return this.constantValue; + case ValueToDataType.FUNCTION: + let result = value; + try { + result = this.valueToDataFunction(value); + } catch (e) {} + return result; + case ValueToDataType.NONE: + return null; + } + } +} + +export abstract class ValueSetter extends ValueAction { + + static fromSettings(ctx: WidgetContext, + settings: SetValueSettings): ValueSetter { + switch (settings.action) { + case SetValueAction.EXECUTE_RPC: + return new ExecuteRpcValueSetter(ctx, settings); + case SetValueAction.SET_ATTRIBUTE: + return new AttributeValueSetter(ctx, settings); + case SetValueAction.ADD_TIME_SERIES: + return new TimeSeriesValueSetter(ctx, settings); + } + } + + private readonly isSimulated: boolean; + private readonly valueToDataConverter: ValueToDataConverter; + + protected constructor(protected ctx: WidgetContext, + protected settings: SetValueSettings) { + super(ctx, settings); + this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; + this.valueToDataConverter = new ValueToDataConverter(settings.valueToData); + } + + setValue(value: V): Observable { + if (this.isSimulated) { + return of(null).pipe(delay(500)); + } else { + return this.doSetValue(this.valueToDataConverter.valueToData(value)).pipe( + catchError(err => { + throw this.handleError(err); + }) + ); + } + } + + protected abstract doSetValue(data: any): Observable; +} + +export class DefaultValueGetter extends ValueGetter { + + private readonly defaultValue: V; + + constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings, valueType, valueObserver); + this.defaultValue = settings.defaultValue; + } + + protected doGetValue(): Observable { + return of(this.defaultValue); + } +} + +export class ExecuteRpcValueGetter extends ValueGetter { + + private readonly executeRpcSettings: RpcSettings; + + constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings, valueType, valueObserver); + this.executeRpcSettings = settings.executeRpc; + } + + protected doGetValue(): Observable { + return this.ctx.controlApi.sendTwoWayCommand(this.executeRpcSettings.method, null, + this.executeRpcSettings.requestTimeout, + this.executeRpcSettings.requestPersistent, + this.executeRpcSettings.persistentPollingInterval).pipe( + catchError((err) => { + throw handleRpcError(this.ctx, err); + }) + ); + } +} + +export abstract class TelemetryValueGetter extends ValueGetter { + + protected targetEntityId: EntityId; + private telemetrySubscriber: TelemetrySubscriber; + + protected constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings, valueType, valueObserver); + const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); + this.targetEntityId = entityInfo?.entityId; + } + + protected doGetValue(): Observable { + if (!this.targetEntityId && !this.ctx.defaultSubscription.rpcEnabled) { + return throwError(() => new Error(this.ctx.translate.instant('widgets.value-action.error.target-entity-is-not-set'))); + } + if (this.targetEntityId) { + const err = validateAttributeScope(this.ctx, this.targetEntityId, this.scope()); + if (err) { + return throwError(() => err); + } + return this.subscribeForTelemetryValue(); + } else { + return of(null); + } + } + + private subscribeForTelemetryValue(): Observable { + this.telemetrySubscriber = + TelemetrySubscriber.createEntityAttributesSubscription(this.ctx.telemetryWsService, this.targetEntityId, + this.scope(), this.ctx.ngZone, [this.getTelemetryValueSettings().key]); + this.telemetrySubscriber.subscribe(); + return this.telemetrySubscriber.attributeData$().pipe( + map((data) => { + let value: V = null; + const entry = data.find(attr => attr.key === this.getTelemetryValueSettings().key); + if (entry) { + value = entry.value; + try { + value = JSON.parse(entry.value); + } catch (_e) {} + } + return value; + }) + ); + } + + protected scope(): TelemetryType { + return LatestTelemetry.LATEST_TELEMETRY; + } + + protected abstract getTelemetryValueSettings(): S; + + destroy() { + if (this.telemetrySubscriber) { + this.telemetrySubscriber.unsubscribe(); + this.telemetrySubscriber = null; + } + super.destroy(); + } +} + +export class AttributeValueGetter extends TelemetryValueGetter { + + constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings, valueType, valueObserver); + } + + protected getTelemetryValueSettings(): GetAttributeValueSettings { + return this.settings.getAttribute; + } + + protected scope(): TelemetryType { + return this.getTelemetryValueSettings().scope; + } + +} + +export class TimeSeriesValueGetter extends TelemetryValueGetter { + + constructor(protected ctx: WidgetContext, + protected settings: GetValueSettings, + protected valueType: ValueType, + protected valueObserver: Partial>) { + super(ctx, settings, valueType, valueObserver); + } + + protected getTelemetryValueSettings(): TelemetryValueSettings { + return this.settings.getTimeSeries; + } +} + +export class ExecuteRpcValueSetter extends ValueSetter { + + private readonly executeRpcSettings: RpcSettings; + + constructor(protected ctx: WidgetContext, + protected settings: SetValueSettings) { + super(ctx, settings); + this.executeRpcSettings = settings.executeRpc; + } + + protected doSetValue(data: any): Observable { + return this.ctx.controlApi.sendOneWayCommand(this.executeRpcSettings.method, data, + this.executeRpcSettings.requestTimeout, + this.executeRpcSettings.requestPersistent, + this.executeRpcSettings.persistentPollingInterval).pipe( + catchError((err) => { + throw handleRpcError(this.ctx, err); + }) + ); + } +} + +export abstract class TelemetryValueSetter extends ValueSetter { + + protected targetEntityId: EntityId; + + protected constructor(protected ctx: WidgetContext, + protected settings: SetValueSettings) { + super(ctx, settings); + const entityInfo = this.ctx.defaultSubscription.getFirstEntityInfo(); + this.targetEntityId = entityInfo?.entityId; + } + + protected doSetValue(data: any): Observable { + if (!this.targetEntityId && !this.ctx.defaultSubscription.rpcEnabled) { + return throwError(() => new Error(this.ctx.translate.instant('widgets.value-action.error.target-entity-is-not-set'))); + } + if (this.targetEntityId) { + const err = validateAttributeScope(this.ctx, this.targetEntityId, this.scope()); + if (err) { + return throwError(() => err); + } + return this.doSetTelemetryValue(data); + } else { + return of(null); + } + } + + protected scope(): TelemetryType { + return LatestTelemetry.LATEST_TELEMETRY; + } + + protected abstract doSetTelemetryValue(data: any): Observable; + +} + +export class AttributeValueSetter extends TelemetryValueSetter { + + private readonly setAttributeValueSettings: SetAttributeValueSettings; + + constructor(protected ctx: WidgetContext, + protected settings: SetValueSettings) { + super(ctx, settings); + this.setAttributeValueSettings = settings.setAttribute; + } + + protected doSetTelemetryValue(data: any): Observable { + const attributes: Array = [{key: this.setAttributeValueSettings.key, value: data}]; + return this.ctx.attributeService.saveEntityAttributes(this.targetEntityId, + this.setAttributeValueSettings.scope, attributes, {ignoreLoading: true, ignoreErrors: true}); + } + + protected scope(): TelemetryType { + return this.setAttributeValueSettings.scope; + } + +} + +export class TimeSeriesValueSetter extends TelemetryValueSetter { + + private readonly putTimeSeriesValueSettings: TelemetryValueSettings; + + constructor(protected ctx: WidgetContext, + protected settings: SetValueSettings) { + super(ctx, settings); + this.putTimeSeriesValueSettings = settings.putTimeSeries; + } + + protected doSetTelemetryValue(data: any): Observable { + const timeSeries: Array = [{key: this.putTimeSeriesValueSettings.key, value: data}]; + return this.ctx.attributeService.saveEntityTimeseries(this.targetEntityId, + LatestTelemetry.LATEST_TELEMETRY, timeSeries, {ignoreLoading: true, ignoreErrors: true}); + } + +} + +const parseError = (ctx: WidgetContext, err: any): string => + ctx.$injector.get(UtilsService).parseException(err).message || 'Unknown Error'; + +const handleRpcError = (ctx: WidgetContext, err: any): Error => { + let reason: string; + if (ctx.defaultSubscription.rpcErrorText) { + reason = ctx.defaultSubscription.rpcErrorText; + } else { + reason = parseError(ctx, err); + } + return new Error(reason); +}; + +const validateAttributeScope = (ctx: WidgetContext, targetEntityId: EntityId, scope?: TelemetryType): Error | null => { + if (targetEntityId.entityType !== EntityType.DEVICE && scope && + ![AttributeScope.SERVER_SCOPE, LatestTelemetry.LATEST_TELEMETRY].includes(scope)) { + const scopeStr = ctx.translate.instant(telemetryTypeTranslationsShort.get(scope)); + const entityType = + ctx.translate.instant(entityTypeTranslations.get(targetEntityId.entityType).type); + const errorMessage = + ctx.translate.instant('widgets.value-action.error.invalid-attribute-scope', {scope: scopeStr, entityType}); + return new Error(errorMessage); + } else { + return null; + } +}; diff --git a/application/src/main/java/org/thingsboard/server/service/install/migrate/EntitiesMigrateService.java b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss similarity index 81% rename from application/src/main/java/org/thingsboard/server/service/install/migrate/EntitiesMigrateService.java rename to ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss index 7e3a1e1ca0..081cb00f0d 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/migrate/EntitiesMigrateService.java +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.install.migrate; - -public interface EntitiesMigrateService { - - void migrate() throws Exception; - +.mdc-linear-progress.tb-action-widget-progress { + position: absolute; + bottom: 0; + left: 0; + right: 0; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.html new file mode 100644 index 0000000000..294ba855fe --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.html @@ -0,0 +1,30 @@ + +
+
+ +
+ + +
diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss similarity index 73% rename from common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java rename to ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss index f073afad17..fcdc355ef9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.queue.settings; - -import lombok.Data; - -@Data -@Deprecated -public class TbRuleEngineQueueSubmitStrategyConfiguration { - - private String type; - private int batchSize; +.tb-action-button-widget { + width: 100%; + height: 100%; + position: relative; + > div.tb-action-button-widget-title-panel { + position: absolute; + top: 12px; + left: 12px; + right: 12px; + z-index: 2; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.ts new file mode 100644 index 0000000000..21f50d0e9c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.ts @@ -0,0 +1,100 @@ +/// +/// Copyright © 2016-2024 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 { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { BasicActionWidgetComponent } from '@home/components/widget/lib/action/action-widget.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { ValueType } from '@shared/models/constants'; +import { + actionButtonDefaultSettings, + ActionButtonWidgetSettings +} from '@home/components/widget/lib/button/action-button-widget.models'; +import { WidgetButtonAppearance } from '@shared/components/button/widget-button.models'; + +@Component({ + selector: 'tb-action-button-widget', + templateUrl: './action-button-widget.component.html', + styleUrls: ['../action/action-widget.scss', './action-button-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ActionButtonWidgetComponent extends + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + settings: ActionButtonWidgetSettings; + + disabled = false; + activated = false; + + appearance: WidgetButtonAppearance; + borderRadius = '4px'; + + constructor(protected imagePipe: ImagePipe, + protected sanitizer: DomSanitizer, + protected cd: ChangeDetectorRef) { + super(cd); + } + + ngOnInit(): void { + super.ngOnInit(); + this.settings = {...actionButtonDefaultSettings, ...this.ctx.settings}; + + this.appearance = this.settings.appearance; + + const activatedStateSettings = + {...this.settings.activatedState, actionLabel: this.ctx.translate.instant('widgets.button-state.activated-state')}; + this.createValueGetter(activatedStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onActivated(value) + }); + + const disabledStateSettings = + {...this.settings.disabledState, actionLabel: this.ctx.translate.instant('widgets.button-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + } + + ngAfterViewInit(): void { + super.ngAfterViewInit(); + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + public onInit() { + super.onInit(); + this.borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.cd.detectChanges(); + } + + public onClick($event: MouseEvent) { + if (!this.ctx.isEdit && !this.ctx.isPreview) { + this.ctx.actionsApi.click($event); + } + } + + private onActivated(value: boolean): void { + this.activated = !!value; + this.cd.markForCheck(); + } + + private onDisabled(value: boolean): void { + this.disabled = !!value; + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts new file mode 100644 index 0000000000..4b3fafa55c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts @@ -0,0 +1,63 @@ +/// +/// Copyright © 2016-2024 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 { + WidgetButtonAppearance, + widgetButtonDefaultAppearance +} from '@shared/components/button/widget-button.models'; +import { DataToValueType, GetValueAction, GetValueSettings } from '@shared/models/action-widget-settings.models'; + +export interface ActionButtonWidgetSettings { + appearance: WidgetButtonAppearance; + activatedState: GetValueSettings; + disabledState: GetValueSettings; +} + +export const actionButtonDefaultSettings: ActionButtonWidgetSettings = { + appearance: widgetButtonDefaultAppearance, + activatedState: { + action: GetValueAction.DO_NOTHING, + defaultValue: false, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + }, + disabledState: { + action: GetValueAction.DO_NOTHING, + defaultValue: false, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.html new file mode 100644 index 0000000000..d48ba2f5e4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.html @@ -0,0 +1,30 @@ + +
+
+ +
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.scss new file mode 100644 index 0000000000..bdaa0c0eea --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.scss @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2024 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-command-button-widget { + width: 100%; + height: 100%; + position: relative; + + > div.tb-command-button-widget-title-panel { + position: absolute; + top: 12px; + left: 12px; + right: 12px; + z-index: 2; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.ts new file mode 100644 index 0000000000..b687001757 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2024 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 { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { ValueType } from '@shared/models/constants'; +import { WidgetButtonAppearance } from '@shared/components/button/widget-button.models'; +import { + commandButtonDefaultSettings, + CommandButtonWidgetSettings +} from '@home/components/widget/lib/button/command-button-widget.models'; + +@Component({ + selector: 'tb-command-button-widget', + templateUrl: './command-button-widget.component.html', + styleUrls: ['../action/action-widget.scss', './command-button-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class CommandButtonWidgetComponent extends + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + settings: CommandButtonWidgetSettings; + + disabled = false; + + appearance: WidgetButtonAppearance; + borderRadius = '4px'; + + private clickValueSetter: ValueSetter; + + constructor(protected imagePipe: ImagePipe, + protected sanitizer: DomSanitizer, + protected cd: ChangeDetectorRef) { + super(cd); + } + + ngOnInit(): void { + super.ngOnInit(); + this.settings = {...commandButtonDefaultSettings, ...this.ctx.settings}; + + this.appearance = this.settings.appearance; + + const disabledStateSettings = + {...this.settings.disabledState, actionLabel: this.ctx.translate.instant('widgets.button-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + + const onClickStateSettings = {...this.settings.onClickState, + actionLabel: this.ctx.translate.instant('widgets.command-button.on-click')}; + this.clickValueSetter = this.createValueSetter(onClickStateSettings); + } + + ngAfterViewInit(): void { + super.ngAfterViewInit(); + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + public onInit() { + super.onInit(); + this.borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.cd.detectChanges(); + } + + public onClick(_$event: MouseEvent) { + if (!this.ctx.isEdit && !this.ctx.isPreview) { + this.updateValue(this.clickValueSetter, null); + } + } + + private onDisabled(value: boolean): void { + this.disabled = !!value; + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts new file mode 100644 index 0000000000..87d4706fc8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts @@ -0,0 +1,71 @@ +/// +/// Copyright © 2016-2024 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 { WidgetButtonAppearance, widgetButtonDefaultAppearance } from '@shared/components/button/widget-button.models'; +import { + DataToValueType, + GetValueAction, + GetValueSettings, SetValueAction, + SetValueSettings, ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; + +export interface CommandButtonWidgetSettings { + appearance: WidgetButtonAppearance; + onClickState: SetValueSettings; + disabledState: GetValueSettings; +} + +export const commandButtonDefaultSettings: CommandButtonWidgetSettings = { + appearance: {...widgetButtonDefaultAppearance, label: 'Send', icon: 'arrow_outward'}, + onClickState: { + action: SetValueAction.EXECUTE_RPC, + executeRpc: { + method: 'setState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + setAttribute: { + key: 'state', + scope: AttributeScope.SHARED_SCOPE + }, + putTimeSeries: { + key: 'state' + }, + valueToData: { + type: ValueToDataType.NONE, + constantValue: true, + valueToDataFunction: '/* Return RPC parameters or attribute/time-series value */\nreturn true;' + } + }, + disabledState: { + action: GetValueAction.DO_NOTHING, + defaultValue: false, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index af3bd41caa..25be87451e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -241,8 +241,8 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit } else { aggValue.value = 'N/A'; } + aggValue.color.update(value); const numeric = formatNumberValue(value, (aggValue.key.decimals || this.ctx.decimals)); - aggValue.color.update(numeric); if (aggValue.showArrow && isDefined(numeric)) { aggValue.upArrow = numeric > 0; aggValue.downArrow = numeric < 0; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts index e43f22ff71..1c65920179 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts @@ -127,11 +127,11 @@ export class GatewayConfigurationComponent implements OnInit { type: [StorageTypes.MEMORY, [Validators.required]], read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]], max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]], - data_folder_path: ['./data/', [Validators.pattern(/^[^\s]+$/)]], + data_folder_path: ['./data/', [Validators.required]], max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - data_file_path: ['./data/data.db', [Validators.pattern(/^[^\s]+$/)]], + data_file_path: ['./data/data.db', [Validators.required]], messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], @@ -240,7 +240,7 @@ export class GatewayConfigurationComponent implements OnInit { storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false}); storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false}); } else if (type === StorageTypes.FILE) { - storageGroup.get('data_folder_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + storageGroup.get('data_folder_path').addValidators([Validators.required]); storageGroup.get('max_file_count').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); storageGroup.get('max_read_records_count').addValidators( @@ -252,7 +252,7 @@ export class GatewayConfigurationComponent implements OnInit { storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false}); storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false}); } else if (type === StorageTypes.SQLITE) { - storageGroup.get('data_file_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + storageGroup.get('data_file_path').addValidators([Validators.required]); storageGroup.get('messages_ttl_check_in_hours').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); storageGroup.get('messages_ttl_in_days').addValidators( diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html index 8182920805..2b9329dc21 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html @@ -80,7 +80,7 @@ + [ngStyle.gt-md]="{ minWidth: '144px', maxWidth: '144px', width: '144px', textAlign: 'center'}"> {{ 'gateway.connectors-table-actions' | translate }} section:not(.table-section) { + max-width: unset; + @media #{$mat-gt-md} { + max-width: 50%; + } + } + .table-section { min-height: 35vh; overflow: hidden; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index a9465fc65b..8071a2f924 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -366,7 +366,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie type: 'success', duration: 1000, verticalPosition: 'top', - horizontalPosition: 'right', + horizontalPosition: 'left', target: 'dashboardRoot', forceDismiss: true })); @@ -381,6 +381,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie if ($event) { $event.stopPropagation(); } + this.initialConnector = attribute.value; const title = `Delete connector ${attribute.key}?`; const content = `All connector data will be deleted.`; this.dialogService.confirm(title, content, 'Cancel', 'Delete').subscribe(result => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts index 3c79883bf3..0933bfe537 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts @@ -106,6 +106,7 @@ export class LiquidLevelWidgetComponent implements OnInit { private volume: number; private tooltipContent: string; private widgetUnits: string; + private volumeUnits: string; private capacityUnits = Object.values(CapacityUnits); @@ -128,7 +129,7 @@ export class LiquidLevelWidgetComponent implements OnInit { this.getData().subscribe(data => { if (data) { - const { svg, volume, units } = data; + const { svg, volume, units, volumeUnits } = data; if (svg && isNotEmptyStr(svg) && this.liquidLevelContent.nativeElement) { const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); jQueryContainerElement.html(svg); @@ -145,6 +146,10 @@ export class LiquidLevelWidgetComponent implements OnInit { this.volume = Number(volume); } + if (volumeUnits) { + this.volumeUnits = volumeUnits; + } + if (units) { this.widgetUnits = units; } @@ -164,7 +169,7 @@ export class LiquidLevelWidgetComponent implements OnInit { this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); } - private getData(): Observable<{ svg: string; volume: number; units: string }> { + private getData(): Observable<{ svg: string; volume: number; units: string; volumeUnits: string}> { if (this.ctx.datasources?.length) { const entityId: EntityId = { entityType: this.ctx.datasources[0].entityType, @@ -308,7 +313,7 @@ export class LiquidLevelWidgetComponent implements OnInit { .pipe(map(attributes => { const shape = extractValue(attributes, this.settings.shapeAttributeName); if (!shape || !svgMapping.has(shape)) { - this.createdErrorMgs(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape)); + this.createdErrorMsg(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape)); return this.settings.selectedShape; } return shape; @@ -318,12 +323,15 @@ export class LiquidLevelWidgetComponent implements OnInit { return of(this.settings.selectedShape); } - private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string }> { + private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string; volumeUnits: string }> { const isVolumeStatic = this.settings.layout !== LevelCardLayout.absolute && this.settings.datasourceUnits === CapacityUnits.percent || this.settings.volumeSource === LiquidWidgetDataSourceType.static; const isUnitStatic = this.settings.layout !== LevelCardLayout.absolute || this.settings.widgetUnitsSource === LiquidWidgetDataSourceType.static; + const isVolumeUnitStatic = this.settings.layout !== LevelCardLayout.absolute + && this.settings.datasourceUnits === CapacityUnits.percent + || this.settings.volumeUnitsSource === LiquidWidgetDataSourceType.static; const attributeKeys: string[] = []; @@ -335,20 +343,29 @@ export class LiquidLevelWidgetComponent implements OnInit { attributeKeys.push(this.settings.widgetUnitsAttributeName); } + if (!isVolumeUnitStatic) { + attributeKeys.push(this.settings.volumeUnitsAttributeName); + } + if (!attributeKeys.length || entityId.id === NULL_UUID) { return of({ volume: this.settings.volumeConstant, + volumeUnits: this.settings.volumeUnits, units: this.settings.units }); } return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeKeys).pipe( map(attributes => { - let volume = isVolumeStatic ? this.settings.volumeConstant : extractValue(attributes, this.settings.volumeAttributeName); - let units = isUnitStatic ? this.settings.units : extractValue(attributes, this.settings.widgetUnitsAttributeName); + let volume = isVolumeStatic ? this.settings.volumeConstant : + extractValue(attributes, this.settings.volumeAttributeName); + let volumeUnits = isVolumeUnitStatic ? this.settings.volumeUnits : + extractValue(attributes, this.settings.volumeUnitsAttributeName); + let units = isUnitStatic ? this.settings.units : + extractValue(attributes, this.settings.widgetUnitsAttributeName); if (!isVolumeStatic && (!volume || !isNumeric(volume) || volume < 0.1)) { - this.createdErrorMgs(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume)); + this.createdErrorMsg(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume)); volume = this.settings.volumeConstant; } @@ -358,20 +375,33 @@ export class LiquidLevelWidgetComponent implements OnInit { units = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits); } if (isUndefinedOrNull(units) || !isNotEmptyStr(units)) { - this.createdErrorMgs(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units)); + this.createdErrorMsg(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units)); units = this.settings.units; } } + if (!isVolumeUnitStatic) { + if (isNotEmptyStr(volumeUnits)) { + const normalizeUnits = volumeUnits.normalize().trim(); + volumeUnits = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits); + } + if (isUndefinedOrNull(volumeUnits) || !isNotEmptyStr(volumeUnits)) { + this.createdErrorMsg(this.settings.widgetUnitsAttributeName, + isUndefinedOrNull(volumeUnits) || isEmptyStr(volumeUnits)); + volumeUnits = this.settings.volumeUnits; + } + } + return { volume, + volumeUnits, units }; }) ); } - private createdErrorMgs(attributeName: string, isEmpty = false) { + private createdErrorMsg(attributeName: string, isEmpty = false) { if (isEmpty) { this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-not-set', {attributeName})); } else { @@ -474,9 +504,14 @@ export class LiquidLevelWidgetComponent implements OnInit { } if (this.settings.layout === LevelCardLayout.absolute) { - const volumeInLiters: number = convertLiters(this.volume, this.settings.volumeUnits as CapacityUnits, ConversionType.to); - const volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from) - .toFixed(this.settings.decimals || 0); + let volume: number | string; + if (this.widgetUnits !== CapacityUnits.percent) { + const volumeInLiters: number = convertLiters(this.volume, this.volumeUnits as CapacityUnits, ConversionType.to); + volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from) + .toFixed(this.settings.decimals || 0); + } else { + volume = this.volume.toFixed(this.settings.decimals || 0); + } const volumeTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.volumeFont), color: this.settings.volumeColor}); @@ -553,7 +588,7 @@ export class LiquidLevelWidgetComponent implements OnInit { private convertInputData(value: any): number { if (this.settings.datasourceUnits !== CapacityUnits.percent) { return (convertLiters(Number(value), this.settings.datasourceUnits, ConversionType.to) / - convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100; + convertLiters(this.volume, this.volumeUnits as CapacityUnits, ConversionType.to)) * 100; } return Number(value); @@ -561,7 +596,7 @@ export class LiquidLevelWidgetComponent implements OnInit { private convertOutputData(value: number): number { if (this.widgetUnits !== CapacityUnits.percent) { - return convertLiters(this.volume * (value / 100), this.settings.volumeUnits, ConversionType.to); + return convertLiters(this.volume * (value / 100), this.volumeUnits as CapacityUnits, ConversionType.to); } return value; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts index 954e228e79..632cfe570d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts @@ -56,6 +56,8 @@ export interface LevelCardWidgetSettings extends WidgetConfig { volumeSource: LiquidWidgetDataSourceType; volumeConstant: number; volumeAttributeName: string; + volumeUnitsSource: LiquidWidgetDataSourceType; + volumeUnitsAttributeName: string; volumeUnits: CapacityUnits; volumeFont: Font; volumeColor: string; @@ -257,8 +259,10 @@ export const levelCardDefaultSettings: LevelCardWidgetSettings = { iconColor: '#5469FF', volumeSource: LiquidWidgetDataSourceType.static, volumeConstant: 500, - volumeUnits: CapacityUnits.liters, volumeAttributeName: 'volume', + volumeUnitsSource: LiquidWidgetDataSourceType.static, + volumeUnitsAttributeName: 'volumeUnits', + volumeUnits: CapacityUnits.liters, volumeFont: { family: 'Roboto', size: 14, @@ -375,9 +379,7 @@ export const convertLiters = (value: number, units: CapacityUnits, conversionTyp return conversionType === ConversionType.to ? value / factor : value * factor; }; -export const extractValue = (attributes: Array, attributeName: string): T | undefined => { - return attributes.find(attr => attr.key === attributeName)?.value; -}; +export const extractValue = (attributes: Array, attributeName: string): T | undefined => attributes.find(attr => attr.key === attributeName)?.value; export const valueContainerStyleDefaults = cssTextFromInlineStyle({ width: '100%', @@ -494,6 +496,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => { const datasourceUnits: string = formGroup.get('datasourceUnits').value; const layout: LevelCardLayout = formGroup.get('layout').value; const volumeSource: LiquidWidgetDataSourceType = formGroup.get('volumeSource').value; + const volumeUnitsSource: LiquidWidgetDataSourceType = formGroup.get('volumeUnitsSource').value; const widgetUnitsSource: LiquidWidgetDataSourceType = formGroup.get('widgetUnitsSource').value; const showTooltipLevel: boolean = formGroup.get('showTooltipLevel').value; const showTooltipDate: boolean = formGroup.get('showTooltipDate').value; @@ -517,7 +520,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => { if (datasourceUnits !== CapacityUnits.percent) { formGroup.get('volumeSource').enable({emitEvent: false}); - formGroup.get('volumeUnits').enable({emitEvent: false}); + formGroup.get('volumeUnitsSource').enable({emitEvent: false}); if (volumeSource === LiquidWidgetDataSourceType.static) { formGroup.get('volumeConstant').enable({emitEvent: false}); formGroup.get('volumeAttributeName').disable({emitEvent: false}); @@ -525,11 +528,20 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => { formGroup.get('volumeConstant').disable({emitEvent: false}); formGroup.get('volumeAttributeName').enable({emitEvent: false}); } + if (volumeUnitsSource === LiquidWidgetDataSourceType.static) { + formGroup.get('volumeUnits').enable({emitEvent: false}); + formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('volumeUnits').disable({emitEvent: false}); + formGroup.get('volumeUnitsAttributeName').enable({emitEvent: false}); + } } else { formGroup.get('volumeSource').disable({emitEvent: false}); formGroup.get('volumeConstant').disable({emitEvent: false}); formGroup.get('volumeAttributeName').disable({emitEvent: false}); + formGroup.get('volumeUnitsSource').disable({emitEvent: false}); formGroup.get('volumeUnits').disable({emitEvent: false}); + formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false}); } if (layout === LevelCardLayout.simple) { @@ -557,7 +569,7 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => { } formGroup.get('volumeSource').enable({emitEvent: false}); - formGroup.get('volumeUnits').enable({emitEvent: false}); + formGroup.get('volumeUnitsSource').enable({emitEvent: false}); if (volumeSource === LiquidWidgetDataSourceType.static) { formGroup.get('volumeConstant').enable({emitEvent: false}); formGroup.get('volumeAttributeName').disable({emitEvent: false}); @@ -565,6 +577,13 @@ export const updatedFormSettingsValidators = (formGroup: FormGroup) => { formGroup.get('volumeConstant').disable({emitEvent: false}); formGroup.get('volumeAttributeName').enable({emitEvent: false}); } + if (volumeUnitsSource === LiquidWidgetDataSourceType.static) { + formGroup.get('volumeUnits').enable({emitEvent: false}); + formGroup.get('volumeUnitsAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('volumeUnits').disable({emitEvent: false}); + formGroup.get('volumeUnitsAttributeName').enable({emitEvent: false}); + } if (formGroup.get('decimals')) { formGroup.get('decimals').enable({emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 8b20b8d93f..5540c1351a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts @@ -32,7 +32,7 @@ import { WidgetUnitedMapSettings } from './map-models'; import { Marker } from './markers'; -import { map, Observable, of, switchMap } from 'rxjs'; +import { map, Observable, of } from 'rxjs'; import { Polyline } from './polyline'; import { Polygon } from './polygon'; import { Circle } from './circle'; @@ -64,6 +64,7 @@ import { MatDialog } from '@angular/material/dialog'; import { FormattedData, ReplaceInfo } from '@shared/models/widget.models'; import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; import { ImagePipe } from '@shared/pipe/image.pipe'; +import { take, tap } from 'rxjs/operators'; export default abstract class LeafletMap { @@ -940,7 +941,12 @@ export default abstract class LeafletMap { this.markersData = markersData; if (this.options.useClusterMarkers) { if (createdMarkers.length) { - this.markersCluster.addLayers(createdMarkers.map(marker => marker.leafletMarker)); + createdMarkers.forEach((marker) => { + marker.createMarkerIconSubject.pipe( + tap(() => this.markersCluster.addLayer(marker.leafletMarker)), + take(1) + ).subscribe(); + }); } if (updatedMarkers.length) { this.markersCluster.refreshClusters(updatedMarkers.map(marker => marker.leafletMarker)); @@ -971,10 +977,15 @@ export default abstract class LeafletMap { } this.markers.set(key, newMarker); if (!this.options.useClusterMarkers) { - this.map.addLayer(newMarker.leafletMarker); - if (this.map.pm.globalDragModeEnabled() && newMarker.leafletMarker.pm) { - newMarker.leafletMarker.pm.enableLayerDrag(); - } + newMarker.createMarkerIconSubject.pipe( + tap(() => { + this.map.addLayer(newMarker.leafletMarker); + if (this.map.pm.globalDragModeEnabled() && newMarker.leafletMarker.pm) { + newMarker.leafletMarker.pm.enableLayerDrag(); + } + }), + take(1) + ).subscribe(); } return newMarker; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts index 23913f75da..582faf6ebd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/markers.ts @@ -23,6 +23,7 @@ import { fillDataPattern, isDefined, isDefinedAndNotNull, processDataPattern, sa import LeafletMap from './leaflet-map'; import { FormattedData } from '@shared/models/widget.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; +import { ReplaySubject } from 'rxjs'; export class Marker { @@ -33,6 +34,7 @@ export class Marker { tooltipOffset: L.LatLngTuple; markerOffset: L.LatLngTuple; tooltip: L.Popup; + createMarkerIconSubject = new ReplaySubject(); constructor(private map: LeafletMap, private location: L.LatLng, @@ -148,6 +150,7 @@ export class Marker { this.labelOffset = [0, -iconInfo.size[1] * this.markerOffset[1] + 10]; } this.updateMarkerLabel(settings); + this.createMarkerIconSubject.next(iconInfo); }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.html new file mode 100644 index 0000000000..48e4106a97 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.html @@ -0,0 +1,26 @@ + +
+
+ +
+
+
+
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.scss new file mode 100644 index 0000000000..d65341a7ba --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.scss @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2024 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-power-button-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + > div:not(.tb-power-button-overlay) { + z-index: 1; + } + .tb-power-button-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-power-button-content { + flex: 1; + min-width: 0; + min-height: 0; + .tb-power-button-shape { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + svg { + .tb-small-shadow { + filter: drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.2)); + } + .tb-shadow { + filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.15)); + } + } + &.tb-power-button-pointer { + svg { + .tb-hover-circle { + cursor: pointer; + } + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts new file mode 100644 index 0000000000..02c58b3e9a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts @@ -0,0 +1,203 @@ +/// +/// Copyright © 2016-2024 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 { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + OnDestroy, + OnInit, + Renderer2, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; +import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; +import { Observable } from 'rxjs'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { ValueType } from '@shared/models/constants'; +import { + powerButtonDefaultSettings, + PowerButtonShape, + powerButtonShapeSize, + PowerButtonWidgetSettings +} from '@home/components/widget/lib/rpc/power-button-widget.models'; +import { SVG, Svg } from '@svgdotjs/svg.js'; + +@Component({ + selector: 'tb-power-button-widget', + templateUrl: './power-button-widget.component.html', + styleUrls: ['../action/action-widget.scss', './power-button-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class PowerButtonWidgetComponent extends + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('powerButtonShape', {static: false}) + powerButtonShape: ElementRef; + + settings: PowerButtonWidgetSettings; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + value = false; + disabled = false; + + private shapeResize$: ResizeObserver; + private drawSvgShapePending = false; + private svgShape: Svg; + private powerButtonSvgShape: PowerButtonShape; + private disabledState = false; + + private onValueSetter: ValueSetter; + private offValueSetter: ValueSetter; + + constructor(protected imagePipe: ImagePipe, + protected sanitizer: DomSanitizer, + private renderer: Renderer2, + protected cd: ChangeDetectorRef) { + super(cd); + } + + ngOnInit(): void { + super.ngOnInit(); + this.settings = {...powerButtonDefaultSettings, ...this.ctx.settings}; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + const getInitialStateSettings = + {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.initial-state')}; + this.createValueGetter(getInitialStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onValue(value) + }); + + const disabledStateSettings = + {...this.settings.disabledState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + + const onUpdateStateSettings = {...this.settings.onUpdateState, + actionLabel: this.ctx.translate.instant('widgets.power-button.power-on')}; + this.onValueSetter = this.createValueSetter(onUpdateStateSettings); + + const offUpdateStateSettings = {...this.settings.offUpdateState, + actionLabel: this.ctx.translate.instant('widgets.power-button.power-off')}; + this.offValueSetter = this.createValueSetter(offUpdateStateSettings); + + this.loading$.subscribe((loading) => { + this.updateDisabledState(loading || this.disabled); + this.cd.markForCheck(); + }); + } + + ngAfterViewInit(): void { + if (this.drawSvgShapePending) { + this.drawSvg(); + } + super.ngAfterViewInit(); + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + super.ngOnDestroy(); + } + + public onInit() { + super.onInit(); + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.powerButtonShape) { + this.drawSvg(); + } else { + this.drawSvgShapePending = true; + } + this.cd.detectChanges(); + } + + private onValue(value: boolean): void { + const newValue = !!value; + if (this.value !== newValue) { + this.value = newValue; + this.powerButtonSvgShape?.setValue(this.value); + this.cd.markForCheck(); + } + } + + private onDisabled(value: boolean): void { + const newDisabled = !!value; + if (this.disabled !== newDisabled) { + this.disabled = newDisabled; + this.updateDisabledState(this.disabled); + this.cd.markForCheck(); + } + } + + private onClick() { + if (!this.ctx.isEdit && !this.ctx.isPreview && !this.disabledState) { + this.onValue(!this.value); + const targetValue = this.value; + const targetSetter = targetValue ? this.onValueSetter : this.offValueSetter; + this.powerButtonSvgShape?.setPressed(true); + this.updateValue(targetSetter, targetValue, { + next: () => { + this.powerButtonSvgShape?.setPressed(false); + this.onValue(targetValue); + }, + error: () => { + this.powerButtonSvgShape?.setPressed(false); + this.onValue(!targetValue); + } + }); + } + } + + private drawSvg() { + this.svgShape = SVG().addTo(this.powerButtonShape.nativeElement).size(powerButtonShapeSize, powerButtonShapeSize); + this.renderer.setStyle(this.svgShape.node, 'overflow', 'visible'); + this.renderer.setStyle(this.svgShape.node, 'user-select', 'none'); + + this.powerButtonSvgShape = PowerButtonShape.fromSettings(this.ctx, this.svgShape, + this.settings, this.value, this.disabledState, () => this.onClick()); + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.powerButtonShape.nativeElement); + this.onResize(); + } + + private updateDisabledState(disabled: boolean) { + this.disabledState = disabled; + this.powerButtonSvgShape?.setDisabled(this.disabledState); + } + + private onResize() { + const shapeWidth = this.powerButtonShape.nativeElement.getBoundingClientRect().width; + const shapeHeight = this.powerButtonShape.nativeElement.getBoundingClientRect().height; + const size = Math.min(shapeWidth, shapeHeight); + const scale = size / powerButtonShapeSize; + this.renderer.setStyle(this.svgShape.node, 'transform', `scale(${scale})`); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts new file mode 100644 index 0000000000..2e8ca0de6d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -0,0 +1,953 @@ +/// +/// Copyright © 2016-2024 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 { BackgroundSettings, BackgroundType } from '@shared/models/widget-settings.models'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { + DataToValueType, + GetValueAction, + GetValueSettings, + SetValueAction, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { Circle, Effect, Element, G, Gradient, Runner, Svg, Text, Timeline } from '@svgdotjs/svg.js'; +import '@svgdotjs/svg.filter.js'; +import tinycolor from 'tinycolor2'; +import { WidgetContext } from '@home/models/widget-component.models'; + +export enum PowerButtonLayout { + default = 'default', + simplified = 'simplified', + outlined = 'outlined', + default_volume = 'default_volume', + simplified_volume = 'simplified_volume', + outlined_volume = 'outlined_volume' +} + +export const powerButtonLayouts = Object.keys(PowerButtonLayout) as PowerButtonLayout[]; + +export const powerButtonLayoutTranslations = new Map( + [ + [PowerButtonLayout.default, 'widgets.power-button.layout-default'], + [PowerButtonLayout.simplified, 'widgets.power-button.layout-simplified'], + [PowerButtonLayout.outlined, 'widgets.power-button.layout-outlined'], + [PowerButtonLayout.default_volume, 'widgets.power-button.layout-default-volume'], + [PowerButtonLayout.simplified_volume, 'widgets.power-button.layout-simplified-volume'], + [PowerButtonLayout.outlined_volume, 'widgets.power-button.layout-outlined-volume'] + ] +); + +export const powerButtonLayoutImages = new Map( + [ + [PowerButtonLayout.default, 'assets/widget/power-button/default-layout.svg'], + [PowerButtonLayout.simplified, 'assets/widget/power-button/simplified-layout.svg'], + [PowerButtonLayout.outlined, 'assets/widget/power-button/outlined-layout.svg'], + [PowerButtonLayout.default_volume, 'assets/widget/power-button/default-volume-layout.svg'], + [PowerButtonLayout.simplified_volume, 'assets/widget/power-button/simplified-volume-layout.svg'], + [PowerButtonLayout.outlined_volume, 'assets/widget/power-button/outlined-volume-layout.svg'] + ] +); + +export interface PowerButtonWidgetSettings { + initialState: GetValueSettings; + disabledState: GetValueSettings; + onUpdateState: SetValueSettings; + offUpdateState: SetValueSettings; + layout: PowerButtonLayout; + mainColorOn: string; + backgroundColorOn: string; + mainColorOff: string; + backgroundColorOff: string; + mainColorDisabled: string; + backgroundColorDisabled: string; + background: BackgroundSettings; +} + +export const powerButtonDefaultSettings: PowerButtonWidgetSettings = { + initialState: { + action: GetValueAction.EXECUTE_RPC, + defaultValue: false, + executeRpc: { + method: 'getState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + }, + disabledState: { + action: GetValueAction.DO_NOTHING, + defaultValue: false, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + }, + onUpdateState: { + action: SetValueAction.EXECUTE_RPC, + executeRpc: { + method: 'setState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + setAttribute: { + key: 'state', + scope: AttributeScope.SHARED_SCOPE + }, + putTimeSeries: { + key: 'state' + }, + valueToData: { + type: ValueToDataType.CONSTANT, + constantValue: true, + valueToDataFunction: '/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;' + } + }, + offUpdateState: { + action: SetValueAction.EXECUTE_RPC, + executeRpc: { + method: 'setState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + setAttribute: { + key: 'state', + scope: AttributeScope.SHARED_SCOPE + }, + putTimeSeries: { + key: 'state' + }, + valueToData: { + type: ValueToDataType.CONSTANT, + constantValue: false, + valueToDataFunction: '/* Convert input boolean value to RPC parameters or attribute/time-series value */ \n return value;' + } + }, + layout: PowerButtonLayout.default, + mainColorOn: '#3F52DD', + backgroundColorOn: '#FFFFFF', + mainColorOff: '#A2A2A2', + backgroundColorOff: '#FFFFFF', + mainColorDisabled: 'rgba(0,0,0,0.12)', + backgroundColorDisabled: '#FFFFFF', + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + +interface PowerButtonColor { + hex: string; + opacity: number; +} + +type PowerButtonState = 'on' | 'off' | 'disabled'; + +interface PowerButtonColorState { + mainColor: PowerButtonColor; + backgroundColor: PowerButtonColor; +} + +type PowerButtonShapeColors = Record; + +const createPowerButtonShapeColors = (settings: PowerButtonWidgetSettings): PowerButtonShapeColors => { + const mainColorOn = tinycolor(settings.mainColorOn); + const backgroundColorOn = tinycolor(settings.backgroundColorOn); + const mainColorOff = tinycolor(settings.mainColorOff); + const backgroundColorOff = tinycolor(settings.backgroundColorOff); + const mainColorDisabled = tinycolor(settings.mainColorDisabled); + const backgroundColorDisabled = tinycolor(settings.backgroundColorDisabled); + return { + on: { + mainColor: {hex: mainColorOn.toHexString(), opacity: mainColorOn.getAlpha()}, + backgroundColor: {hex: backgroundColorOn.toHexString(), opacity: backgroundColorOn.getAlpha()}, + }, + off: { + mainColor: {hex: mainColorOff.toHexString(), opacity: mainColorOff.getAlpha()}, + backgroundColor: {hex: backgroundColorOff.toHexString(), opacity: backgroundColorOff.getAlpha()}, + }, + disabled: { + mainColor: {hex: mainColorDisabled.toHexString(), opacity: mainColorDisabled.getAlpha()}, + backgroundColor: {hex: backgroundColorDisabled.toHexString(), opacity: backgroundColorDisabled.getAlpha()}, + } + }; +}; + +export const powerButtonShapeSize = 110; +const cx = powerButtonShapeSize / 2; +const cy = powerButtonShapeSize / 2; + +const powerButtonAnimation = (element: Element): Runner => element.animate(200, 0, 'now'); + +export abstract class PowerButtonShape { + + static fromSettings(ctx: WidgetContext, + svgShape: Svg, + settings: PowerButtonWidgetSettings, + value: boolean, + disabled: boolean, + onClick: () => void): PowerButtonShape { + switch (settings.layout) { + case PowerButtonLayout.default: + return new DefaultPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.simplified: + return new SimplifiedPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.outlined: + return new OutlinedPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.default_volume: + return new DefaultVolumePowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.simplified_volume: + return new SimplifiedVolumePowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.outlined_volume: + return new OutlinedVolumePowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + } + } + + protected readonly colors: PowerButtonShapeColors; + protected readonly onLabel: string; + protected readonly offLabel: string; + + protected backgroundShape: Circle; + protected hoverShape: Circle; + protected hovered = false; + protected pressed = false; + protected forcePressed = false; + + protected constructor(protected widgetContext: WidgetContext, + protected svgShape: Svg, + protected settings: PowerButtonWidgetSettings, + protected value: boolean, + protected disabled: boolean, + protected onClick: () => void) { + this.colors = createPowerButtonShapeColors(this.settings); + this.onLabel = this.widgetContext.translate.instant('widgets.power-button.on-label').toUpperCase(); + this.offLabel = this.widgetContext.translate.instant('widgets.power-button.off-label').toUpperCase(); + this._drawShape(); + } + + public setValue(value: boolean) { + if (this.value !== value) { + this.value = value; + this._drawState(); + } + } + + public setDisabled(disabled: boolean) { + if (this.disabled !== disabled) { + this.disabled = disabled; + this._drawState(); + } + } + + public setPressed(pressed: boolean) { + if (this.forcePressed !== pressed) { + this.forcePressed = pressed; + if (this.forcePressed && !this.pressed) { + this.onPressStart(); + } else if (!this.forcePressed && !this.pressed) { + this.onPressEnd(); + } + } + } + + private _drawShape() { + + this.backgroundShape = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + + this.drawShape(); + + this.hoverShape = this.svgShape.circle(powerButtonShapeSize).center(cx, cy).addClass('tb-hover-circle') + .fill({color: '#000000', opacity: 0}); + this.hoverShape.on('mouseover', () => { + this.hovered = true; + if (!this.disabled) { + this.hoverShape.timeline().finish(); + this.hoverShape.animate(200).attr({'fill-opacity': 0.06}); + } + }); + this.hoverShape.on('mouseout', () => { + this.hovered = false; + this.hoverShape.timeline().finish(); + this.hoverShape.animate(200).attr({'fill-opacity': 0}); + this._cancelPressed(); + }); + this.hoverShape.on('touchmove', (event: TouchEvent) => { + const touch = event.touches[0]; + const element = document.elementFromPoint(touch.pageX,touch.pageY); + if (this.hoverShape.node !== element) { + this._cancelPressed(); + } + }); + this.hoverShape.on('touchcancel', () => { + this._cancelPressed(); + }); + this.hoverShape.on('mousedown touchstart', (event: Event) => { + if (event.type === 'mousedown') { + if ((event as MouseEvent).button !== 0) { + return; + } + } + if (!this.disabled && !this.pressed) { + this.pressed = true; + if (!this.forcePressed) { + this.onPressStart(); + } + } + }); + this.hoverShape.on('mouseup touchend touchcancel', () => { + if (this.pressed && !this.disabled) { + this.onClick(); + } + this._cancelPressed(); + }); + this._drawState(); + } + + private _cancelPressed() { + if (this.pressed) { + this.pressed = false; + if (!this.forcePressed) { + this.onPressEnd(); + } + } + } + + private _drawState() { + let colorState: PowerButtonColorState; + if (this.disabled) { + colorState = this.colors.disabled; + } else { + colorState = this.value ? this.colors.on : this.colors.off; + } + this.drawBackgroundState(colorState.backgroundColor); + this.drawColorState(colorState.mainColor); + if (this.value) { + this.drawOn(); + } else { + this.drawOff(); + } + if (this.disabled) { + this.hoverShape.timeline().finish(); + this.hoverShape.attr({'fill-opacity': 0}); + } else if (this.hovered) { + this.hoverShape.timeline().finish(); + this.hoverShape.animate(200).attr({'fill-opacity': 0.06}); + } + } + + private drawBackgroundState(backgroundColor: PowerButtonColor) { + this.backgroundShape.attr({ fill: backgroundColor.hex, 'fill-opacity': backgroundColor.opacity}); + } + + protected drawShape() {} + + protected drawColorState(_mainColor: PowerButtonColor) {} + + protected drawOff() {} + + protected drawOn() {} + + protected onPressStart() {} + + protected onPressEnd() {} + + protected createMask(shape: Element, maskElements: Element[]) { + const mask = + this.svgShape.mask().add(this.svgShape.rect().width('100%').height('100%').fill('#fff')); + maskElements.forEach(e => { + mask.add(e.fill('#000').attr({'fill-opacity': 1})); + }); + shape.maskWith(mask); + } + + protected createOnLabel(fontWeight = '500'): Text { + return this.createLabel(this.onLabel, fontWeight); + } + + protected createOffLabel(fontWeight = '500'): Text { + return this.createLabel(this.offLabel, fontWeight); + } + + private createLabel(text: string, fontWeight = '500'): Text { + return this.svgShape.text(text).font({ + family: 'Roboto', + weight: fontWeight, + style: 'normal', + size: '22px' + }).attr({x: '50%', y: '50%', 'text-anchor': 'middle', 'dominant-baseline': 'middle'}); + } + +} + +class InnerShadowCircle { + + private shadowCircle: Circle; + private blurEffect: Effect; + private offsetEffect: Effect; + private floodEffect: Effect; + + constructor(private svgShape: Svg, + private diameter: number, + private centerX: number, + private centerY: number, + private blur = 6, + private shadowOpacity = 0.6, + private dx = 0, + private dy = 0, + private shadowColor = '#000') { + + this.shadowCircle = this.svgShape.circle(this.diameter).center(this.centerX, this.centerY) + .fill({color: '#fff', opacity: 1}).stroke({width: 0}); + + this.shadowCircle.filterWith(add => { + add.x('-50%').y('-50%').width('200%').height('200%'); + let effect: Effect = add.componentTransfer(components => { + components.funcA({ type: 'table', tableValues: '1 0' }); + }).in(add.$fill); + effect = effect.gaussianBlur(this.blur, this.blur).attr({stdDeviation: this.blur}); + this.blurEffect = effect; + effect = effect.offset(this.dx, this.dy); + this.offsetEffect = effect; + effect = effect.flood(this.shadowColor, this.shadowOpacity); + this.floodEffect = effect; + effect = effect.composite(this.offsetEffect, 'in'); + effect.composite(add.$sourceAlpha, 'in'); + add.merge(m => { + m.mergeNode(add.$fill); + m.mergeNode(); + }); + }); + } + + public timeline(tl: Timeline): void { + this.blurEffect.timeline(tl); + this.offsetEffect.timeline(tl); + this.floodEffect.timeline(tl); + } + + public animate(blur: number, opacity: number, dx = 0, dy = 0): Runner { + powerButtonAnimation(this.blurEffect).attr({stdDeviation: blur}); + powerButtonAnimation(this.offsetEffect).attr({dx, dy}); + return powerButtonAnimation(this.floodEffect).attr({'flood-opacity': opacity}); + } + + public animateRestore(): Runner { + return this.animate(this.blur, this.shadowOpacity, this.dx, this.dy); + } + + public show(): void { + this.shadowCircle.show(); + } + + public hide(): void { + this.shadowCircle.hide(); + } + +} + +class DefaultPowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private offLabelShape: Text; + private onCircleShape: Circle; + private onLabelShape: Text; + private pressedShadow: InnerShadowCircle; + private pressedTimeline: Timeline; + private centerGroup: G; + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); + this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 20) + .center(cx, cy); + this.onLabelShape = this.createOnLabel(); + this.createMask(this.onCircleShape, [this.onLabelShape]); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 20, cx, cy, 0, 0); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.pressedShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor) { + this.outerBorder.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + this.outerBorderMask.radius((powerButtonShapeSize - 20)/2); + this.onCircleShape.hide(); + this.centerGroup.show(); + } + + protected drawOn() { + this.outerBorderMask.radius((powerButtonShapeSize - 2)/2); + this.centerGroup.hide(); + this.onCircleShape.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + const pressedScale = 0.75; + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedShadow.animate(6, 0.6); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); + } + +} + +class SimplifiedPowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private onCircleShape: Circle; + private offLabelShape: Text; + private onLabelShape: Text; + private pressedShadow: InnerShadowCircle; + private pressedTimeline: Timeline; + private centerGroup: G; + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 4).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); + this.onCircleShape = this.svgShape.circle(powerButtonShapeSize).center(cx, cy); + this.onLabelShape = this.createOnLabel(); + this.createMask(this.onCircleShape, [this.onLabelShape]); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 0, 0); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.pressedShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor) { + this.outerBorder.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + this.onCircleShape.hide(); + this.outerBorder.show(); + this.centerGroup.show(); + } + + protected drawOn() { + this.centerGroup.hide(); + this.outerBorder.hide(); + this.onCircleShape.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + const pressedScale = 0.75; + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedShadow.animate(6, 0.6); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); + } +} + +class OutlinedPowerButtonShape extends PowerButtonShape { + private outerBorder: Circle; + private outerBorderMask: Circle; + private innerBorder: Circle; + private innerBorderMask: Circle; + private offLabelShape: Text; + private onCircleShape: Circle; + private onLabelShape: Text; + private pressedShadow: InnerShadowCircle; + private pressedTimeline: Timeline; + private centerGroup: G; + private onCenterGroup: G; + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 2).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.innerBorder = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.innerBorderMask = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); + this.createMask(this.innerBorder, [this.innerBorderMask]); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); + this.onCenterGroup = this.svgShape.group(); + this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 28).center(cx, cy) + .addTo(this.onCenterGroup); + this.onLabelShape = this.createOnLabel(); + this.createMask(this.onCircleShape, [this.onLabelShape]); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 24, cx, cy, 0, 0); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onCenterGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.pressedShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor) { + this.outerBorder.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.innerBorder.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + this.onCenterGroup.hide(); + this.centerGroup.show(); + } + + protected drawOn() { + this.centerGroup.hide(); + this.onCenterGroup.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + const pressedScale = 0.75; + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + this.pressedShadow.animate(6, 0.6); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); + } +} + +class DefaultVolumePowerButtonShape extends PowerButtonShape { + private outerBorder: Circle; + private outerBorderMask: Circle; + private outerBorderGradient: Gradient; + private innerBorder: Circle; + private innerBorderMask: Circle; + private innerBorderGradient: Gradient; + private innerShadow: InnerShadowCircle; + //private innerShadowGradient: Gradient; + //private innerShadowGradientStop: Stop; + private offLabelShape: Text; + private onCircleShape: Circle; + private onLabelShape: Text; + private pressedTimeline: Timeline; + private centerGroup: G; + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.outerBorderGradient = this.svgShape.gradient('linear', (add) => { + add.stop(0, '#CCCCCC', 1); + add.stop(1, '#FFFFFF', 1); + }).from(0.268, 0.92).to(0.832, 0.1188); + this.innerBorder = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.innerBorderMask = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); + this.createMask(this.innerBorder, [this.innerBorderMask]); + this.innerBorderGradient = this.svgShape.gradient('linear', (add) => { + add.stop(0, '#CCCCCC', 1); + add.stop(1, '#FFFFFF', 1); + }).from(0.832, 0.1188).to(0.268, 0.92); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel('400').addTo(this.centerGroup); + this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); + this.onLabelShape = this.createOnLabel('400'); + this.createMask(this.onCircleShape, [this.onLabelShape]); + this.innerShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 24, cx, cy, 3, 0.3); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.innerShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor){ + if (this.disabled) { + this.backgroundShape.removeClass('tb-small-shadow'); + if (!this.forcePressed) { + this.innerShadow.hide(); + } + this.outerBorder.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.innerBorder.attr({fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } else { + this.backgroundShape.addClass('tb-small-shadow'); + this.innerShadow.show(); + this.outerBorder.fill(this.outerBorderGradient); + this.outerBorder.attr({ 'fill-opacity': 1 }); + this.innerBorder.fill(this.innerBorderGradient); + this.innerBorder.attr({ 'fill-opacity': 1 }); + } + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + this.onCircleShape.hide(); + this.centerGroup.show(); + this.innerBorder.show(); + } + + protected drawOn() { + if (this.disabled) { + this.innerBorder.hide(); + } else { + this.innerBorder.show(); + } + this.centerGroup.hide(); + this.onCircleShape.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + this.innerShadow.show(); + const pressedScale = 0.75; + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.innerShadow.animate(6, 0.6); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.innerShadow.animateRestore().after(() => { + if (this.disabled) { + this.innerShadow.hide(); + } + }); + } + +} + +class SimplifiedVolumePowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private offLabelShape: Text; + private onLabelShape: Text; + private innerShadow: InnerShadowCircle; + private pressedShadow: InnerShadowCircle; + private pressedTimeline: Timeline; + private centerGroup: G; + private onCenterGroup: G; + + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({color: '#FAFAFA', opacity: 1}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 4).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); + this.onCenterGroup = this.svgShape.group(); + this.onLabelShape = this.createOnLabel().addTo(this.onCenterGroup); + this.innerShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 3, 0.3); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 0, 0); + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onCenterGroup.timeline(this.pressedTimeline); + this.pressedShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor){ + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + if (!this.pressed) { + this.backgroundShape.addClass('tb-shadow'); + } + this.innerShadow.hide(); + this.onCenterGroup.hide(); + this.centerGroup.show(); + } + + protected drawOn() { + this.backgroundShape.removeClass('tb-shadow'); + this.centerGroup.hide(); + this.onCenterGroup.show(); + this.innerShadow.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + const pressedScale = 0.75; + if (!this.value) { + this.backgroundShape.removeClass('tb-shadow'); + } + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale}); + this.pressedShadow.animate(8, 0.4); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + this.pressedShadow.animateRestore().after(() => { + if (!this.value) { + this.backgroundShape.addClass('tb-shadow'); + } + }); + } +} + +class OutlinedVolumePowerButtonShape extends PowerButtonShape { + private outerBorder: Circle; + private outerBorderMask: Circle; + private outerBorderGradient: Gradient; + private innerBorder: Circle; + private innerBorderMask: Circle; + private offLabelShape: Text; + private onCircleShape: Circle; + private onLabelShape: Text; + private pressedShadow: InnerShadowCircle; + private pressedTimeline: Timeline; + private centerGroup: G; + private onCenterGroup: G; + + protected drawShape() { + this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy); + this.createMask(this.outerBorder, [this.outerBorderMask]); + this.outerBorderGradient = this.svgShape.gradient('linear', (add) => { + add.stop(0, '#CCCCCC', 1); + add.stop(1, '#FFFFFF', 1); + }).from(0.268, 0.92).to(0.832, 0.1188); + this.innerBorder = this.svgShape.circle(powerButtonShapeSize - 20).center(cx, cy) + .fill({opacity: 0}).stroke({width: 0}); + this.innerBorderMask = this.svgShape.circle(powerButtonShapeSize - 30).center(cx, cy); + this.createMask(this.innerBorder, [this.innerBorderMask]); + this.centerGroup = this.svgShape.group(); + this.offLabelShape = this.createOffLabel('800').addTo(this.centerGroup); + this.onCenterGroup = this.svgShape.group(); + this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 30).center(cx, cy) + .addTo(this.onCenterGroup); + this.onLabelShape = this.createOnLabel('800'); + this.createMask(this.onCircleShape, [this.onLabelShape]); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 30, cx, cy, 0, 0); + this.backgroundShape.addClass('tb-small-shadow'); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onCenterGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.pressedShadow.timeline(this.pressedTimeline); + } + + protected drawColorState(mainColor: PowerButtonColor){ + if (this.disabled) { + this.outerBorder.attr({ fill: '#000000', 'fill-opacity': 0.03}); + } else { + this.outerBorder.fill(this.outerBorderGradient); + this.outerBorder.attr({ 'fill-opacity': 1 }); + } + this.innerBorder.attr({fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected drawOff() { + this.onCenterGroup.hide(); + this.centerGroup.show(); + this.innerBorder.show(); + } + + protected drawOn() { + this.innerBorder.hide(); + this.centerGroup.hide(); + this.onCenterGroup.show(); + } + + protected onPressStart() { + this.pressedTimeline.finish(); + const pressedScale = 0.75; + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + this.pressedShadow.animate(6, 0.6); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widget.models.ts deleted file mode 100644 index f47e654fdb..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/rpc-widget.models.ts +++ /dev/null @@ -1,626 +0,0 @@ -/// -/// Copyright © 2016-2024 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 { - AttributeData, - AttributeScope, - LatestTelemetry, - telemetryTypeTranslationsShort -} from '@shared/models/telemetry/telemetry.models'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; -import { catchError, delay, map, share } from 'rxjs/operators'; -import { UtilsService } from '@core/services/utils.service'; -import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnInit, TemplateRef } from '@angular/core'; -import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; -import { ImagePipe } from '@shared/pipe/image.pipe'; -import { DomSanitizer } from '@angular/platform-browser'; -import { - RpcActionSettings, - RpcGetAttributeSettings, - RpcSetAttributeSettings, - RpcSettings, - RpcStateToParamsSettings, - RpcStateToParamsType, - RpcStateWidgetSettings, - RpcTelemetrySettings, - RpcUpdateStateAction -} from '@shared/models/rpc-widget-settings.models'; -import { - RpcDataToStateSettings, - RpcDataToStateType, - RpcInitialStateAction, - RpcInitialStateSettings, - RpcStateBehaviourSettings, - RpcUpdateStateSettings -} from '@app/shared/models/rpc-widget-settings.models'; -import { ValueType } from '@shared/models/constants'; -import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; - -@Directive() -// eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class BasicRpcStateWidgetComponent> implements OnInit, AfterViewInit { - - @Input() - ctx: WidgetContext; - - @Input() - widgetTitlePanel: TemplateRef; - - settings: S; - - behaviorApi: RpcStateBehaviorApi; - loading$: Observable; - - backgroundStyle$: Observable; - overlayStyle: ComponentStyle = {}; - - value: V; - - error = ''; - - protected constructor(protected imagePipe: ImagePipe, - protected sanitizer: DomSanitizer, - protected cd: ChangeDetectorRef) { - } - - ngOnInit(): void { - this.ctx.$scope.rpcWidget = this; - this.settings = {...this.defaultSettings(), ...this.ctx.settings}; - this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); - this.overlayStyle = overlayStyle(this.settings.background.overlay); - - const behaviourSettings: RpcStateBehaviourSettings = { - initialState: this.initialState(), - updateStateByValue: val => this.getUpdateStateSettingsForValue(val) - }; - - const callbacks: RpcStateCallbacks = { - onStateValue: val => this.setValue(val), - onError: err => this.onError(err), - validateStateValue: val => this.validateValue(val) - }; - - this.behaviorApi = new RpcStateBehaviorApi(this.defaultValue(), this.ctx, - behaviourSettings, callbacks, this.stateValueType()); - this.loading$ = this.behaviorApi.loading$.pipe(share()); - } - - ngAfterViewInit(): void { - this.behaviorApi.initState(); - } - - public onInit() { - const borderRadius = this.ctx.$widgetElement.css('borderRadius'); - this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; - this.cd.detectChanges(); - } - - public clearError() { - this.error = ''; - } - - public updateValue() { - this.behaviorApi.updateState(this.value); - } - - private onError(error: string) { - this.error = error; - this.ctx.detectChanges(); - } - - protected abstract defaultSettings(): S; - - protected abstract initialState(): RpcInitialStateSettings; - - protected abstract getUpdateStateSettingsForValue(value: V): RpcUpdateStateSettings; - - protected stateValueType(): ValueType { - return ValueType.BOOLEAN; - } - - protected defaultValue(): V { - return null; - } - - protected setValue(value: V) { - this.value = value; - } - - protected validateValue(value: any): V { - return value; - } - -} - -export abstract class RpcHasLoading { - - public get loading$() { - return this.loadingSubject.asObservable(); - } - - protected loadingSubject = new BehaviorSubject(false); -} - -export interface RpcStateCallbacks { - onStateValue: (value: V) => void; - validateStateValue: (value: any) => V; - onError: (error: string) => void; -} - -export class RpcStateBehaviorApi extends RpcHasLoading { - - private readonly initialStateGetter: RpcInitialStateGetter; - private readonly stateUpdatersMap: Map>; - - constructor(private state: V, - private ctx: WidgetContext, - private settings: RpcStateBehaviourSettings, - private callbacks: RpcStateCallbacks, - stateValueType: ValueType) { - super(); - this.initialStateGetter = RpcInitialStateGetter.fromSettings(ctx, settings.initialState, stateValueType, callbacks); - this.stateUpdatersMap = new Map>(); - } - - initState() { - if (this.ctx.defaultSubscription.targetEntityId || this.ctx.defaultSubscription.rpcEnabled) { - this.loadingSubject.next(true); - this.initialStateGetter.initState().subscribe( - { - next: (value) => { - this.state = value; - this.loadingSubject.next(false); - this.callbacks.onStateValue(value); - this.ctx.detectChanges(); - }, - error: (err: any) => { - this.loadingSubject.next(false); - const message = parseError(this.ctx, err); - this.callbacks.onError(message); - } - } - ); - } else { - this.callbacks.onError(this.ctx.translate.instant('widgets.rpc-state.error.target-entity-is-not-set')); - } - } - - updateState(value: V) { - this.callbacks.onError(null); - let updater: RpcStateUpdater; - const updateStateSettings = this.settings.updateStateByValue(value); - if (updateStateSettings) { - updater = this.stateUpdatersMap.get(updateStateSettings); - if (!updater) { - updater = RpcStateUpdater.fromSettings(this.ctx, updateStateSettings); - this.stateUpdatersMap.set(updateStateSettings, updater); - } - } - if (updater) { - this.loadingSubject.next(true); - updater.updateState(value).subscribe( - { - next: () => { - this.state = value; - this.loadingSubject.next(false); - this.ctx.detectChanges(); - }, - error: (err: any) => { - this.loadingSubject.next(false); - const message = parseError(this.ctx, err); - this.callbacks.onStateValue(this.state); - this.callbacks.onError(message); - this.ctx.detectChanges(); - } - }); - } - } - -} - -type RpcDataToStateFunction = (data: any) => V; - -export class RpcDataToStateConverter { - - private readonly dataToStateFunction: RpcDataToStateFunction; - private readonly compareToValue: any; - - constructor(private settings: RpcDataToStateSettings, - private stateValueType: ValueType, - private callbacks: RpcStateCallbacks) { - this.compareToValue = settings.compareToValue; - switch (settings.type) { - case RpcDataToStateType.FUNCTION: - try { - this.dataToStateFunction = new Function('data', settings.dataToStateFunction) as RpcDataToStateFunction; - } catch (e) { - this.dataToStateFunction = (data) => data; - } - break; - case RpcDataToStateType.NONE: - break; - } - } - - dataToStateValue(data: any): V { - let result: V; - switch (this.settings.type) { - case RpcDataToStateType.FUNCTION: - result = data; - try { - result = this.dataToStateFunction(!!data ? JSON.parse(data) : data); - } catch (e) {} - break; - case RpcDataToStateType.NONE: - result = data; - break; - } - if (this.stateValueType === ValueType.BOOLEAN) { - result = (result === this.compareToValue) as any; - } - result = this.callbacks.validateStateValue(result); - return result; - } -} - -export abstract class RpcAction { - - protected constructor(protected ctx: WidgetContext, - protected settings: RpcActionSettings) {} - - handleError(err: any): Error { - const reason = parseError(this.ctx, err); - let errorMessage = this.ctx.translate.instant('widgets.rpc-state.error.failed-to-perform-action', - {actionLabel: this.settings.actionLabel}); - if (reason) { - errorMessage += '
' + reason; - } - return new Error(errorMessage); - } -} - -export abstract class RpcInitialStateGetter extends RpcAction { - - static fromSettings(ctx: WidgetContext, - settings: RpcInitialStateSettings, - stateValueType: ValueType, - callbacks: RpcStateCallbacks): RpcInitialStateGetter { - switch (settings.action) { - case RpcInitialStateAction.DO_NOTHING: - return new RpcDefaultStateGetter(ctx, settings, stateValueType, callbacks); - case RpcInitialStateAction.EXECUTE_RPC: - return new ExecuteRpcStateGetter(ctx, settings, stateValueType, callbacks); - case RpcInitialStateAction.GET_ATTRIBUTE: - return new RpcAttributeStateGetter(ctx, settings, stateValueType, callbacks); - case RpcInitialStateAction.GET_TIME_SERIES: - return new RpcTimeSeriesStateGetter(ctx, settings, stateValueType, callbacks); - } - } - - private readonly isSimulated: boolean; - private readonly dataConverter: RpcDataToStateConverter; - - protected constructor(protected ctx: WidgetContext, - protected settings: RpcInitialStateSettings, - protected stateValueType: ValueType, - protected callbacks: RpcStateCallbacks) { - super(ctx, settings); - this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; - if (this.settings.action !== RpcInitialStateAction.DO_NOTHING) { - this.dataConverter = new RpcDataToStateConverter(settings.dataToState, stateValueType, this.callbacks); - } - } - - initState(): Observable { - const stateObservable: Observable = this.isSimulated ? of(null).pipe(delay(500)) : this.doGetState(); - return stateObservable.pipe( - map((data) => { - if (this.dataConverter) { - return this.dataConverter.dataToStateValue(data); - } else { - return data; - } - }), - catchError(err => { - throw this.handleError(err); - }) - ); - } - - protected abstract doGetState(): Observable; -} - -type RpcStateToParamsFunction = (state: V) => any; - -export class RpcStateToParamsConverter { - - private readonly constantValue: any; - private readonly stateToParamsFunction: RpcStateToParamsFunction; - - constructor(protected settings: RpcStateToParamsSettings) { - switch (settings.type) { - case RpcStateToParamsType.CONSTANT: - this.constantValue = this.settings.constantValue; - break; - case RpcStateToParamsType.FUNCTION: - try { - this.stateToParamsFunction = new Function('value', settings.stateToParamsFunction) as RpcStateToParamsFunction; - } catch (e) { - this.stateToParamsFunction = (data) => data; - } - break; - case RpcStateToParamsType.NONE: - break; - } - } - - stateToParams(state: V): any { - switch (this.settings.type) { - case RpcStateToParamsType.CONSTANT: - return this.constantValue; - case RpcStateToParamsType.FUNCTION: - let result = state; - try { - result = this.stateToParamsFunction(state); - } catch (e) {} - return result; - case RpcStateToParamsType.NONE: - return null; - } - } -} - -export abstract class RpcStateUpdater extends RpcAction { - - static fromSettings(ctx: WidgetContext, - settings: RpcUpdateStateSettings): RpcStateUpdater { - switch (settings.action) { - case RpcUpdateStateAction.EXECUTE_RPC: - return new ExecuteRpcStateUpdater(ctx, settings); - case RpcUpdateStateAction.SET_ATTRIBUTE: - return new RpcAttributeStateUpdater(ctx, settings); - case RpcUpdateStateAction.ADD_TIME_SERIES: - return new RpcTimeSeriesStateUpdater(ctx, settings); - } - } - - private readonly isSimulated: boolean; - private readonly paramsConverter: RpcStateToParamsConverter; - - protected constructor(protected ctx: WidgetContext, - protected settings: RpcUpdateStateSettings) { - super(ctx, settings); - this.isSimulated = this.ctx.$injector.get(UtilsService).widgetEditMode; - this.paramsConverter = new RpcStateToParamsConverter(settings.stateToParams); - } - - updateState(state: V): Observable { - if (this.isSimulated) { - return of(null).pipe(delay(500)); - } else { - return this.doUpdateState(this.paramsConverter.stateToParams(state)).pipe( - catchError(err => { - throw this.handleError(err); - }) - ); - } - } - - protected abstract doUpdateState(params: any): Observable; -} - -export class RpcDefaultStateGetter extends RpcInitialStateGetter { - - private readonly defaultValue: V; - - constructor(protected ctx: WidgetContext, - protected settings: RpcInitialStateSettings, - protected stateValueType: ValueType, - protected callbacks: RpcStateCallbacks) { - super(ctx, settings, stateValueType, callbacks); - this.defaultValue = settings.defaultValue; - } - - protected doGetState(): Observable { - return of(this.defaultValue); - } -} - -export class ExecuteRpcStateGetter extends RpcInitialStateGetter { - - private readonly executeRpcSettings: RpcSettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcInitialStateSettings, - protected stateValueType: ValueType, - protected callbacks: RpcStateCallbacks) { - super(ctx, settings, stateValueType, callbacks); - this.executeRpcSettings = settings.executeRpc; - } - - protected doGetState(): Observable { - return this.ctx.controlApi.sendTwoWayCommand(this.executeRpcSettings.method, null, - this.executeRpcSettings.requestTimeout, - this.executeRpcSettings.requestPersistent, - this.executeRpcSettings.persistentPollingInterval).pipe( - catchError((err) => { - throw handleRpcError(this.ctx, err); - }) - ); - } -} - -export class RpcAttributeStateGetter extends RpcInitialStateGetter { - - private readonly getAttributeSettings: RpcGetAttributeSettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcInitialStateSettings, - protected stateValueType: ValueType, - protected callbacks: RpcStateCallbacks) { - super(ctx, settings, stateValueType, callbacks); - this.getAttributeSettings = settings.getAttribute; - } - - protected doGetState(): Observable { - if (this.ctx.defaultSubscription.targetEntityId) { - const err = validateAttributeScope(this.ctx, this.getAttributeSettings.scope); - if (err) { - return throwError(() => err); - } - return this.ctx.attributeService.getEntityAttributes(this.ctx.defaultSubscription.targetEntityId, - this.getAttributeSettings.scope, [this.getAttributeSettings.key], {ignoreLoading: true, ignoreErrors: true}) - .pipe( - map((data) => data.find(attr => attr.key === this.getAttributeSettings.key)?.value) - ); - } else { - return of(null); - } - } - -} - -export class RpcTimeSeriesStateGetter extends RpcInitialStateGetter { - - private readonly getTimeSeriesSettings: RpcTelemetrySettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcInitialStateSettings, - protected stateValueType: ValueType, - protected callbacks: RpcStateCallbacks) { - super(ctx, settings, stateValueType, callbacks); - this.getTimeSeriesSettings = settings.getTimeSeries; - } - - protected doGetState(): Observable { - if (this.ctx.defaultSubscription.targetEntityId) { - return this.ctx.attributeService.getEntityTimeseriesLatest(this.ctx.defaultSubscription.targetEntityId, - [this.getTimeSeriesSettings.key], true, {ignoreLoading: true, ignoreErrors: true}) - .pipe( - map((data) => { - let value: any = null; - if (data[this.getTimeSeriesSettings.key]) { - const dataSet = data[this.getTimeSeriesSettings.key]; - if (dataSet.length) { - value = dataSet[0].value; - } - } - return value; - }) - ); - } else { - return of(null); - } - } - -} - -export class ExecuteRpcStateUpdater extends RpcStateUpdater { - - private readonly executeRpcSettings: RpcSettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcUpdateStateSettings) { - super(ctx, settings); - this.executeRpcSettings = settings.executeRpc; - } - - protected doUpdateState(params: any): Observable { - return this.ctx.controlApi.sendOneWayCommand(this.executeRpcSettings.method, params, - this.executeRpcSettings.requestTimeout, - this.executeRpcSettings.requestPersistent, - this.executeRpcSettings.persistentPollingInterval).pipe( - catchError((err) => { - throw handleRpcError(this.ctx, err); - }) - ); - } -} - -export class RpcAttributeStateUpdater extends RpcStateUpdater { - - private readonly setAttributeSettings: RpcSetAttributeSettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcUpdateStateSettings) { - super(ctx, settings); - this.setAttributeSettings = settings.setAttribute; - } - - protected doUpdateState(params: any): Observable { - if (this.ctx.defaultSubscription.targetEntityId) { - const err = validateAttributeScope(this.ctx, this.setAttributeSettings.scope); - if (err) { - return throwError(() => err); - } - const attributes: Array = [{key: this.setAttributeSettings.key, value: params}]; - return this.ctx.attributeService.saveEntityAttributes(this.ctx.defaultSubscription.targetEntityId, - this.setAttributeSettings.scope, attributes, {ignoreLoading: true, ignoreErrors: true}); - } else { - return of(null); - } - } - -} - -export class RpcTimeSeriesStateUpdater extends RpcStateUpdater { - - private readonly putTimeSeriesSettings: RpcTelemetrySettings; - - constructor(protected ctx: WidgetContext, - protected settings: RpcUpdateStateSettings) { - super(ctx, settings); - this.putTimeSeriesSettings = settings.putTimeSeries; - } - - protected doUpdateState(params: any): Observable { - if (this.ctx.defaultSubscription.targetEntityId) { - const timeSeries: Array = [{key: this.putTimeSeriesSettings.key, value: params}]; - return this.ctx.attributeService.saveEntityTimeseries(this.ctx.defaultSubscription.targetEntityId, - LatestTelemetry.LATEST_TELEMETRY, timeSeries, {ignoreLoading: true, ignoreErrors: true}); - } else { - return of(null); - } - } - -} - -const parseError = (ctx: WidgetContext, err: any): string => - ctx.$injector.get(UtilsService).parseException(err).message || 'Unknown Error'; - -const handleRpcError = (ctx: WidgetContext, err: any): Error => { - let reason: string; - if (ctx.defaultSubscription.rpcErrorText) { - reason = ctx.defaultSubscription.rpcErrorText; - } else { - reason = parseError(ctx, err); - } - return new Error(reason); -}; - -const validateAttributeScope = (ctx: WidgetContext, scope?: AttributeScope): Error | null => { - if (ctx.defaultSubscription.targetEntityId.entityType !== EntityType.DEVICE && scope && scope !== AttributeScope.SERVER_SCOPE) { - const scopeStr = ctx.translate.instant(telemetryTypeTranslationsShort.get(scope)); - const entityType = - ctx.translate.instant(entityTypeTranslations.get(ctx.defaultSubscription.targetEntityId.entityType).type); - const errorMessage = - ctx.translate.instant('widgets.rpc-state.error.invalid-attribute-scope', {scope: scopeStr, entityType}); - return new Error(errorMessage); - } else { - return null; - } -}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html index ee27f83458..fd9aaca4a6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html @@ -15,28 +15,26 @@ limitations under the License. --> -
-
+
+
- {{ icon }} -
{{ label$ | async }}
+ {{ icon }} +
{{ label$ | async }}
-
{{ offLabel }}
- +
{{ offLabel }}
+ -
{{ onLabel }}
-
-
- -
-
-
- +
{{ onLabel }}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss index 884bea0e43..27207e5f9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.scss @@ -29,51 +29,15 @@ $switchColorDisabled: var(--tb-single-switch-color-disabled, #D5D7E5); align-items: center; justify-content: center; padding: 18px 24px; + &.auto-scale { + padding: 0; + } > div:not(.tb-single-switch-overlay), > tb-icon { z-index: 1; } .tb-single-switch-overlay { position: absolute; - top: 12px; - left: 12px; - bottom: 12px; - right: 12px; - } - .tb-single-switch-progress { - position: absolute; - bottom: 0; - left: 0; - right: 0; - } - .tb-single-switch-error-container { - position: absolute; - bottom: 0; - left: 0; - right: 0; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - .tb-single-switch-error-panel { - display: flex; - padding: 4px 4px 4px 12px; - justify-content: center; - align-items: center; - gap: 4px; - border-radius: 4px; - background-color: #fff2f3; - box-shadow: -2px 2px 4px 0px rgba(0,0,0,0.2); - .tb-single-switch-error-text { - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 16px; - color: rgba(209, 39, 48, 1); - } - .tb-single-switch-error-clear { - color: rgba(209, 39, 48, 1); - } - } + inset: 12px; } > div.tb-single-switch-title-panel { position: absolute; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts index 7794e51ebc..2c15634893 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts @@ -25,33 +25,38 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; -import { BasicRpcStateWidgetComponent } from '@home/components/widget/lib/rpc/rpc-widget.models'; +import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import { singleSwitchDefaultSettings, SingleSwitchLayout, SingleSwitchWidgetSettings } from '@home/components/widget/lib/rpc/single-switch-widget.models'; -import { ComponentStyle, iconStyle, textStyle } from '@shared/models/widget-settings.models'; +import { + backgroundStyle, + ComponentStyle, + iconStyle, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; import { Observable } from 'rxjs'; import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; -import cssjs from '@core/css/css'; -import { hashCode } from '@core/utils'; -import { RpcInitialStateSettings, RpcUpdateStateSettings } from '@shared/models/rpc-widget-settings.models'; import { ValueType } from '@shared/models/constants'; +import { UtilsService } from '@core/services/utils.service'; +const initialSwitchHeight = 60; const horizontalLayoutPadding = 48; const verticalLayoutPadding = 36; @Component({ selector: 'tb-single-switch-widget', templateUrl: './single-switch-widget.component.html', - styleUrls: ['./single-switch-widget.component.scss'], + styleUrls: ['../action/action-widget.scss', './single-switch-widget.component.scss'], encapsulation: ViewEncapsulation.None }) export class SingleSwitchWidgetComponent extends - BasicRpcStateWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild('singleSwitchPanel', {static: false}) singleSwitchPanel: ElementRef; @@ -65,6 +70,15 @@ export class SingleSwitchWidgetComponent extends @ViewChild('singleSwitchToggleRow', {static: false}) singleSwitchToggleRow: ElementRef; + settings: SingleSwitchWidgetSettings; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + overlayInset = '12px'; + + value = false; + disabled = false; + layout: SingleSwitchLayout; showIcon = false; @@ -83,20 +97,33 @@ export class SingleSwitchWidgetComponent extends offLabel = ''; offLabelStyle: ComponentStyle = {}; + disabledColor = 'rgba(0, 0, 0, 0.38)'; + autoScale = false; private panelResize$: ResizeObserver; + private onValueSetter: ValueSetter; + private offValueSetter: ValueSetter; + + private singleSwitchCssClass: string; + constructor(protected imagePipe: ImagePipe, protected sanitizer: DomSanitizer, private renderer: Renderer2, + private utils: UtilsService, protected cd: ChangeDetectorRef, private elementRef: ElementRef) { - super(imagePipe, sanitizer, cd); + super(cd); } ngOnInit(): void { super.ngOnInit(); + this.settings = {...singleSwitchDefaultSettings, ...this.ctx.settings}; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.layout = this.settings.layout; this.autoScale = this.settings.autoScale; @@ -104,22 +131,18 @@ export class SingleSwitchWidgetComponent extends this.showLabel = this.settings.showLabel; this.label$ = this.ctx.registerLabelPattern(this.settings.label, this.label$); this.labelStyle = textStyle(this.settings.labelFont); - this.labelStyle.color = this.settings.labelColor; this.showIcon = this.settings.showIcon; this.icon = this.settings.icon; this.iconStyle = iconStyle(this.settings.iconSize, this.settings.iconSizeUnit ); - this.iconStyle.color = this.settings.iconColor; this.showOnLabel = this.settings.showOnLabel; this.onLabel = this.settings.onLabel; this.onLabelStyle = textStyle(this.settings.onLabelFont); - this.onLabelStyle.color = this.settings.onLabelColor; this.showOffLabel = this.settings.showOffLabel; this.offLabel = this.settings.offLabel; this.offLabelStyle = textStyle(this.settings.offLabelFont); - this.offLabelStyle.color = this.settings.offLabelColor; const switchVariablesCss = `.tb-single-switch-panel {\n`+ `--tb-single-switch-tumbler-color-on: ${this.settings.tumblerColorOn};\n`+ `--tb-single-switch-tumbler-color-off: ${this.settings.tumblerColorOff};\n`+ @@ -128,12 +151,28 @@ export class SingleSwitchWidgetComponent extends `--tb-single-switch-color-off: ${this.settings.switchColorOff};\n`+ `--tb-single-switch-color-disabled: ${this.settings.switchColorDisabled};\n`+ `}`; - const cssParser = new cssjs(); - cssParser.testMode = false; - const namespace = 'single-switch-' + hashCode(switchVariablesCss); - cssParser.cssPreviewNamespace = namespace; - cssParser.createStyleElement(namespace, switchVariablesCss); - this.renderer.addClass(this.elementRef.nativeElement, namespace); + this.singleSwitchCssClass = + this.utils.applyCssToElement(this.renderer, this.elementRef.nativeElement, 'tb-single-switch', switchVariablesCss); + + const getInitialStateSettings = + {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.initial-state')}; + this.createValueGetter(getInitialStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onValue(value) + }); + + const disabledStateSettings = + {...this.settings.disabledState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + + const onUpdateStateSettings = {...this.settings.onUpdateState, + actionLabel: this.ctx.translate.instant('widgets.rpc-state.turn-on')}; + this.onValueSetter = this.createValueSetter(onUpdateStateSettings); + + const offUpdateStateSettings = {...this.settings.offUpdateState, + actionLabel: this.ctx.translate.instant('widgets.rpc-state.turn-off')}; + this.offValueSetter = this.createValueSetter(offUpdateStateSettings); } ngAfterViewInit(): void { @@ -156,47 +195,62 @@ export class SingleSwitchWidgetComponent extends if (this.panelResize$) { this.panelResize$.disconnect(); } + if (this.singleSwitchCssClass) { + this.utils.clearCssElement(this.renderer, this.singleSwitchCssClass); + } + super.ngOnDestroy(); } - protected stateValueType(): ValueType { - return ValueType.BOOLEAN; - } - - protected defaultValue(): boolean { - return false; - } - - protected defaultSettings(): SingleSwitchWidgetSettings { - return {...singleSwitchDefaultSettings}; + public onInit() { + super.onInit(); + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); } - protected initialState(): RpcInitialStateSettings { - return {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.initial-state')}; + public onToggleChange(event: MouseEvent) { + if (!this.ctx.isEdit && !this.ctx.isPreview) { + event.preventDefault(); + const targetValue = this.value; + const targetSetter = targetValue ? this.onValueSetter : this.offValueSetter; + this.updateValue(targetSetter, targetValue, { + next: () => this.onValue(targetValue), + error: () => this.onValue(!targetValue) + }); + } } - protected getUpdateStateSettingsForValue(value: boolean): RpcUpdateStateSettings { - const targetSettings = value ? this.settings.onUpdateState : this.settings.offUpdateState; - return {...targetSettings, actionLabel: this.ctx.translate.instant(value ? 'widgets.rpc-state.turn-on' : 'widgets.rpc-state.turn-off')}; + private onValue(value: boolean): void { + this.value = !!value; + this.cd.markForCheck(); } - protected validateValue(value: any): boolean { - return !!value; + private onDisabled(value: boolean): void { + this.disabled = !!value; + this.cd.markForCheck(); } private onResize() { - const panelWidth = this.singleSwitchPanel.nativeElement.getBoundingClientRect().width - horizontalLayoutPadding; - const panelHeight = this.singleSwitchPanel.nativeElement.getBoundingClientRect().height - verticalLayoutPadding; + const height = this.singleSwitchPanel.nativeElement.getBoundingClientRect().height; + const switchScale = height / initialSwitchHeight; + const paddingScale = Math.min(switchScale, 1); + const panelWidth = this.singleSwitchPanel.nativeElement.getBoundingClientRect().width - (horizontalLayoutPadding * paddingScale); + const panelHeight = this.singleSwitchPanel.nativeElement.getBoundingClientRect().height - (verticalLayoutPadding * paddingScale); this.renderer.setStyle(this.singleSwitchContent.nativeElement, 'transform', `scale(1)`); + this.renderer.setStyle(this.singleSwitchContent.nativeElement, 'width', 'auto'); let contentWidth = this.singleSwitchToggleRow.nativeElement.getBoundingClientRect().width; let contentHeight = this.singleSwitchToggleRow.nativeElement.getBoundingClientRect().height; if (this.showIcon || this.showLabel) { contentWidth += (8 + this.singleSwitchLabelRow.nativeElement.getBoundingClientRect().width); contentHeight = Math.max(contentHeight, this.singleSwitchLabelRow.nativeElement.getBoundingClientRect().height); } - const scale = Math.min(panelWidth / contentWidth, panelHeight / contentHeight); + const maxScale = Math.max(1, switchScale); + const scale = Math.min(Math.min(panelWidth / contentWidth, panelHeight / contentHeight), maxScale); const width = panelWidth / scale; this.renderer.setStyle(this.singleSwitchContent.nativeElement, 'width', width + 'px'); this.renderer.setStyle(this.singleSwitchContent.nativeElement, 'transform', `scale(${scale})`); + this.overlayInset = (Math.floor(12 * paddingScale * 100) / 100) + 'px'; + this.cd.markForCheck(); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts index 57c2af3b5e..7897574cac 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.models.ts @@ -14,16 +14,16 @@ /// limitations under the License. /// -import { BackgroundType, cssUnit, Font } from '@shared/models/widget-settings.models'; +import { BackgroundSettings, BackgroundType, cssUnit, Font } from '@shared/models/widget-settings.models'; import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { - RpcDataToStateType, - RpcInitialStateAction, - RpcStateToParamsType, - RpcStateWidgetSettings, - RpcUpdateStateAction, - RpcUpdateStateSettings -} from '@shared/models/rpc-widget-settings.models'; + DataToValueType, + GetValueAction, + GetValueSettings, + SetValueAction, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; export enum SingleSwitchLayout { right = 'right', @@ -49,9 +49,11 @@ export const singleSwitchLayoutImages = new Map( ] ); -export interface SingleSwitchWidgetSettings extends RpcStateWidgetSettings { - onUpdateState: RpcUpdateStateSettings; - offUpdateState: RpcUpdateStateSettings; +export interface SingleSwitchWidgetSettings { + initialState: GetValueSettings; + disabledState: GetValueSettings; + onUpdateState: SetValueSettings; + offUpdateState: SetValueSettings; layout: SingleSwitchLayout; autoScale: boolean; showLabel: boolean; @@ -77,11 +79,12 @@ export interface SingleSwitchWidgetSettings extends RpcStateWidgetSettings +
+
+
+ +
+
+
+
{{ valueText }}
+
+
+
+ {{ leftIcon }} +
+
+ + + +
+
+
{{ settings.tickMin }}
+
+
+
{{ settings.tickMax }}
+
+
+
+
+ {{ rightIcon }} +
+
+
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.scss new file mode 100644 index 0000000000..0d7115fccb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.scss @@ -0,0 +1,135 @@ +/** + * Copyright © 2016-2024 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. + */ +$mainColor: var(--tb-slider-main-color, #5469FF); +$hoverRippleColor: var(--tb-slider-hover-ripple-color, rgba(84, 105, 255, 0.05)); +$focusRippleColor: var(--tb-slider-focus-ripple-color, rgba(84, 105, 255, 0.2)); +$backgroundColor: var(--tb-slider-background-color, #CCD2FF); +$tickMarksColor: var(--tb-slider-tick-marks-color, #5469FF); + +$mainColorDisabled: var(--tb-slider-main-color-disabled, #9BA2B0); +$backgroundColorDisabled: var(--tb-slider-background-color-disabled, #D5D7E5); + +.tb-slider-panel { + + .mat-mdc-slider.mat-primary.tb-slider { + --mdc-slider-active-track-color: #{$mainColor}; + --mdc-slider-handle-color: #{$mainColor}; + --mdc-slider-focus-handle-color: #{$mainColor}; + --mdc-slider-hover-handle-color: #{$mainColor}; + + --mdc-slider-with-tick-marks-inactive-container-color: #{$tickMarksColor}; + --mat-mdc-slider-ripple-color: #{$mainColor}; + + --mat-mdc-slider-hover-ripple-color: #{$hoverRippleColor}; + --mat-mdc-slider-focus-ripple-color: #{$focusRippleColor}; + + --mdc-slider-inactive-track-color: #{$backgroundColor}; + + --mdc-slider-disabled-active-track-color: #{$mainColorDisabled}; + --mdc-slider-disabled-handle-color: #{$mainColorDisabled}; + + --mdc-slider-disabled-inactive-track-color: #{$backgroundColorDisabled}; + --mdc-slider-with-tick-marks-disabled-container-color: #{$mainColorDisabled}; + + --mdc-slider-handle-width: 16px; + --mdc-slider-handle-height: 16px; + } + + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + padding: 20px 24px 24px 24px; + gap: 8px; + > div:not(.tb-slider-overlay), > tb-icon { + z-index: 1; + } + .tb-slider-overlay { + position: absolute; + inset: 12px; + } + div.tb-slider-title-panel { + z-index: 2; + } + .tb-slider-content { + flex: 1; + min-height: 0; + display: flex; + position: relative; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + .tb-slider-value-container { + min-height: 0; + } + .tb-slider-value { + white-space: nowrap; + } + .tb-slider-container { + align-self: stretch; + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 8px; + &.tb-min-height { + height: 6px; + } + .tb-slider-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + .mat-mdc-slider.tb-slider { + margin: 0; + height: 6px; + min-height: 6px; + min-width: 0; + &.mdc-slider--disabled { + opacity: 1; + } + .mdc-slider__track--inactive { + opacity: 1; + } + .mdc-slider__tick-marks { + .mdc-slider__tick-mark--active { + display: none; + } + .mdc-slider__tick-mark--inactive { + opacity: 1; + } + } + .mdc-slider__thumb.mat-mdc-slider-visual-thumb { + top: -21px; + .mat-ripple { + overflow: visible; + } + } + .mdc-slider__value-indicator-text { + white-space: nowrap; + } + } + .tb-slider-ticks { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: space-between; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts new file mode 100644 index 0000000000..37b5319d44 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts @@ -0,0 +1,347 @@ +/// +/// Copyright © 2016-2024 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 { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + OnDestroy, + OnInit, + Renderer2, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; +import { + backgroundStyle, + ComponentStyle, + iconStyle, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { Observable } from 'rxjs'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { ValueType } from '@shared/models/constants'; +import { UtilsService } from '@core/services/utils.service'; +import { + SliderLayout, + sliderWidgetDefaultSettings, + SliderWidgetSettings +} from '@home/components/widget/lib/rpc/slider-widget.models'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import tinycolor from 'tinycolor2'; + +@Component({ + selector: 'tb-slider-widget', + templateUrl: './slider-widget.component.html', + styleUrls: ['../action/action-widget.scss', './slider-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SliderWidgetComponent extends + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('sliderContent', {static: false}) + sliderContent: ElementRef; + + @ViewChild('sliderValueContainer', {static: false}) + sliderValueContainer: ElementRef; + + @ViewChild('sliderValue', {static: false}) + sliderValue: ElementRef; + + @ViewChild('sliderTickMinContainer', {static: false}) + sliderTickMinContainer: ElementRef; + + @ViewChild('sliderTickMin', {static: false}) + sliderTickMin: ElementRef; + + @ViewChild('sliderTickMaxContainer', {static: false}) + sliderTickMaxContainer: ElementRef; + + @ViewChild('sliderTickMax', {static: false}) + sliderTickMax: ElementRef; + + @ViewChild('leftSliderIconContainer', {static: false, read: ElementRef}) + leftSliderIconContainer: ElementRef; + + @ViewChild('leftSliderIcon', {static: false, read: ElementRef}) + leftSliderIcon: ElementRef; + + @ViewChild('rightSliderIconContainer', {static: false, read: ElementRef}) + rightSliderIconContainer: ElementRef; + + @ViewChild('rightSliderIcon', {static: false, read: ElementRef}) + rightSliderIcon: ElementRef; + + settings: SliderWidgetSettings; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + value: number = null; + private prevValue: number = null; + + disabled = false; + + layout: SliderLayout; + + showValue = true; + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + + showLeftRightIcon = false; + leftIcon = ''; + leftIconStyle: ComponentStyle = {}; + rightIcon = ''; + rightIconStyle: ComponentStyle = {}; + + showTicks = true; + ticksStyle: ComponentStyle = {}; + + sliderStep: number = undefined; + + autoScale = false; + + showWidgetTitlePanel = this.widgetComponent.dashboardWidget.showWidgetTitlePanel; + + sliderValueText = this._sliderValueText.bind(this); + + private panelResize$: ResizeObserver; + + private valueSetter: ValueSetter; + + private sliderCssClass: string; + + constructor(protected imagePipe: ImagePipe, + protected sanitizer: DomSanitizer, + private renderer: Renderer2, + private utils: UtilsService, + private widgetComponent: WidgetComponent, + protected cd: ChangeDetectorRef, + private elementRef: ElementRef) { + super(cd); + } + + ngOnInit(): void { + super.ngOnInit(); + this.settings = {...sliderWidgetDefaultSettings, ...this.ctx.settings}; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.layout = this.settings.layout; + + this.autoScale = this.settings.autoScale; + + this.showValue = this.layout !== SliderLayout.simplified && this.settings.showValue; + this.valueStyle = textStyle(this.settings.valueFont); + this.valueStyle.color = this.settings.valueColor; + + this.showLeftRightIcon = this.layout === SliderLayout.extended; + if (this.showLeftRightIcon) { + this.leftIcon = this.settings.leftIcon; + this.leftIconStyle = iconStyle(this.settings.leftIconSize, this.settings.leftIconSizeUnit ); + this.rightIcon = this.settings.rightIcon; + this.rightIconStyle = iconStyle(this.settings.rightIconSize, this.settings.rightIconSizeUnit ); + if (!this.autoScale) { + const leftIconMargin = this.settings.leftIconSize / 2 + (this.settings.leftIconSizeUnit || 'px'); + this.leftIconStyle.marginTop = `calc(-${leftIconMargin} + 3px)`; + const rightIconMargin = this.settings.rightIconSize / 2 + (this.settings.rightIconSizeUnit || 'px'); + this.rightIconStyle.marginTop = `calc(-${rightIconMargin} + 3px)`; + } + } + + this.showTicks = this.settings.showTicks; + if (this.showTicks) { + this.ticksStyle = textStyle(this.settings.ticksFont); + this.ticksStyle.color = this.settings.ticksColor; + } + + if (this.settings.showTickMarks) { + const range = this.settings.tickMax - this.settings.tickMin; + this.sliderStep = range / (this.settings.tickMarksCount - 1); + } + + const mainColorInstance = tinycolor(this.settings.mainColor); + const hoverRippleColor = mainColorInstance.clone().setAlpha(mainColorInstance.getAlpha() * 0.05).toRgbString(); + const focusRippleColor = mainColorInstance.clone().setAlpha(mainColorInstance.getAlpha() * 0.2).toRgbString(); + + const sliderVariablesCss = `.tb-slider-panel {\n`+ + `--tb-slider-main-color: ${this.settings.mainColor};\n`+ + `--tb-slider-background-color: ${this.settings.backgroundColor};\n`+ + `--tb-slider-hover-ripple-color: ${hoverRippleColor};\n`+ + `--tb-slider-focus-ripple-color: ${focusRippleColor};\n`+ + `--tb-slider-tick-marks-color: ${this.settings.tickMarksColor};\n`+ + `--tb-slider-main-color-disabled: ${this.settings.mainColorDisabled};\n`+ + `--tb-slider-background-disabled: ${this.settings.backgroundColorDisabled};\n`+ + `}`; + this.sliderCssClass = + this.utils.applyCssToElement(this.renderer, this.elementRef.nativeElement, 'tb-slider', sliderVariablesCss); + + const getInitialStateSettings = + {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.slider.initial-value')}; + this.createValueGetter(getInitialStateSettings, ValueType.INTEGER, { + next: (value) => this.onValue(value) + }); + + const disabledStateSettings = + {...this.settings.disabledState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + + const valueChangeSettings = {...this.settings.valueChange, + actionLabel: this.ctx.translate.instant('widgets.slider.on-value-change')}; + this.valueSetter = this.createValueSetter(valueChangeSettings); + } + + ngAfterViewInit(): void { + if (this.autoScale) { + this.panelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.panelResize$.observe(this.sliderContent.nativeElement); + if (this.showValue) { + this.panelResize$.observe(this.sliderValueContainer.nativeElement); + } + this.onResize(); + } + super.ngAfterViewInit(); + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } + if (this.sliderCssClass) { + this.utils.clearCssElement(this.renderer, this.sliderCssClass); + } + super.ngOnDestroy(); + } + + public onInit() { + super.onInit(); + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onSliderChange() { + this.updateValueText(); + if (!this.ctx.isEdit && !this.ctx.isPreview) { + const prevValue = this.prevValue; + const targetValue = this.value; + this.updateValue(this.valueSetter, targetValue, { + next: () => this.onValue(targetValue), + error: () => this.onValue(prevValue) + }); + } + } + + private _sliderValueText(value: number): string { + return formatValue(value, this.settings.valueDecimals, this.settings.valueUnits, false); + } + + private onValue(value: number): void { + this.value = value; + this.prevValue = value; + this.updateValueText(); + this.cd.markForCheck(); + } + + private updateValueText() { + if (isDefinedAndNotNull(this.value) && isNumeric(this.value)) { + this.valueText = formatValue(this.value, this.settings.valueDecimals, this.settings.valueUnits, false); + } else { + this.valueText = 'N/A'; + } + } + + private onDisabled(value: boolean): void { + this.disabled = !!value; + this.cd.markForCheck(); + } + + private onResize() { + const panelWidth = this.sliderContent.nativeElement.getBoundingClientRect().width; + const panelHeight = this.sliderContent.nativeElement.getBoundingClientRect().height; + + if (this.showValue) { + this.resetScale(this.sliderValueContainer.nativeElement, this.sliderValue.nativeElement); + } + + if (this.showLeftRightIcon) { + this.resetScale(this.leftSliderIconContainer.nativeElement, this.leftSliderIcon.nativeElement); + this.resetScale(this.rightSliderIconContainer.nativeElement, this.rightSliderIcon.nativeElement); + } + + if (this.showTicks) { + this.resetScale(this.sliderTickMinContainer.nativeElement, this.sliderTickMin.nativeElement); + this.resetScale(this.sliderTickMaxContainer.nativeElement, this.sliderTickMax.nativeElement); + } + + let minAspect = 0.2; + let avgContentHeight = 35; + if (this.showTicks) { + minAspect += 0.1; + avgContentHeight += 20; + } + if (this.showValue) { + minAspect += 0.1; + avgContentHeight += 50; + } + const aspect = Math.min(panelHeight / panelWidth, minAspect); + const targetHeight = panelWidth * aspect; + const scale = targetHeight / avgContentHeight; + + if (this.showValue) { + this.updateScale(this.sliderValueContainer.nativeElement, this.sliderValue.nativeElement, scale); + } + if (this.showLeftRightIcon) { + const leftIconContainerRect = this.leftSliderIconContainer.nativeElement.getBoundingClientRect(); + const leftIconContainerMarginTop = -(leftIconContainerRect.width * scale) / 2 + 3; + this.renderer.setStyle(this.leftSliderIconContainer.nativeElement, 'marginTop', `${leftIconContainerMarginTop}px`); + this.updateScale(this.leftSliderIconContainer.nativeElement, this.leftSliderIcon.nativeElement, scale, true); + const rightIconContainerRect = this.rightSliderIconContainer.nativeElement.getBoundingClientRect(); + const rightIconContainerMarginTop = -(rightIconContainerRect.width * scale) / 2 + 3; + this.renderer.setStyle(this.rightSliderIconContainer.nativeElement, 'marginTop', `${rightIconContainerMarginTop}px`); + this.updateScale(this.rightSliderIconContainer.nativeElement, this.rightSliderIcon.nativeElement, scale, true); + } + if (this.showTicks) { + this.updateScale(this.sliderTickMinContainer.nativeElement, this.sliderTickMin.nativeElement, scale); + this.updateScale(this.sliderTickMaxContainer.nativeElement, this.sliderTickMax.nativeElement, scale); + } + } + + private resetScale(container: HTMLElement, element: HTMLElement): void { + this.renderer.setStyle(container, 'width', ''); + this.renderer.setStyle(container, 'height', ''); + this.renderer.setStyle(element, 'transform', ''); + } + + private updateScale(container: HTMLElement, element: HTMLElement, scale: number, sameHeight = false): void { + const rect = container.getBoundingClientRect(); + this.renderer.setStyle(container, 'width', `${rect.width * scale}px`); + this.renderer.setStyle(container, 'height', `${(sameHeight ? rect.width : rect.height) * scale}px`); + this.renderer.setStyle(element, 'transform', `scale(${scale})`); + this.renderer.setStyle(element, 'transform-origin', 'left top'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts new file mode 100644 index 0000000000..c6fe1e290a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts @@ -0,0 +1,196 @@ +/// +/// Copyright © 2016-2024 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 { + DataToValueType, + GetValueAction, + GetValueSettings, + SetValueAction, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { BackgroundSettings, BackgroundType, cssUnit, Font } from '@shared/models/widget-settings.models'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; + +export enum SliderLayout { + default = 'default', + extended = 'extended', + simplified = 'simplified' +} + +export const sliderLayouts = Object.keys(SliderLayout) as SliderLayout[]; + +export const sliderLayoutTranslations = new Map( + [ + [SliderLayout.default, 'widgets.slider.layout-default'], + [SliderLayout.extended, 'widgets.slider.layout-extended'], + [SliderLayout.simplified, 'widgets.slider.layout-simplified'] + ] +); + +export const sliderLayoutImages = new Map( + [ + [SliderLayout.default, 'assets/widget/slider/default-layout.svg'], + [SliderLayout.extended, 'assets/widget/slider/extended-layout.svg'], + [SliderLayout.simplified, 'assets/widget/slider/simplified-layout.svg'] + ] +); + +export interface SliderWidgetSettings { + initialState: GetValueSettings; + disabledState: GetValueSettings; + valueChange: SetValueSettings; + layout: SliderLayout; + autoScale: boolean; + showValue: boolean; + valueUnits: string; + valueDecimals: number; + valueFont: Font; + valueColor: string; + showTicks: boolean; + tickMin: number; + tickMax: number; + ticksFont: Font; + ticksColor: string; + showTickMarks: boolean; + tickMarksCount: number; + tickMarksColor: string; + mainColor: string; + backgroundColor: string; + mainColorDisabled: string; + backgroundColorDisabled: string; + leftIcon: string; + leftIconSize: number; + leftIconSizeUnit: cssUnit; + leftIconColor: string; + rightIcon: string; + rightIconSize: number; + rightIconSizeUnit: cssUnit; + rightIconColor: string; + background: BackgroundSettings; +} + +export const sliderWidgetDefaultSettings: SliderWidgetSettings = { + initialState: { + action: GetValueAction.EXECUTE_RPC, + defaultValue: 0, + executeRpc: { + method: 'getState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return integer value */\nreturn data;' + } + }, + disabledState: { + action: GetValueAction.DO_NOTHING, + defaultValue: false, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + }, + valueChange: { + action: SetValueAction.EXECUTE_RPC, + executeRpc: { + method: 'setState', + requestTimeout: 5000, + requestPersistent: false, + persistentPollingInterval: 1000 + }, + setAttribute: { + key: 'state', + scope: AttributeScope.SHARED_SCOPE + }, + putTimeSeries: { + key: 'state' + }, + valueToData: { + type: ValueToDataType.VALUE, + constantValue: 0, + valueToDataFunction: '/* Convert input integer value to RPC parameters or attribute/time-series value */\nreturn value;' + } + }, + layout: SliderLayout.default, + autoScale: true, + showValue: true, + valueUnits: '%', + valueDecimals: 0, + valueFont: { + family: 'Roboto', + size: 36, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '36px' + }, + valueColor: 'rgba(0, 0, 0, 0.87)', + showTicks: true, + tickMin: 0, + tickMax: 100, + ticksFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + ticksColor: 'rgba(0,0,0,0.54)', + showTickMarks: true, + tickMarksCount: 11, + tickMarksColor: '#5469FF', + mainColor: '#5469FF', + backgroundColor: '#CCD2FF', + mainColorDisabled: '#9BA2B0', + backgroundColorDisabled: '#D5D7E5', + leftIcon: 'lightbulb', + leftIconSize: 24, + leftIconSizeUnit: 'px', + leftIconColor: '#5469FF', + rightIcon: 'mdi:lightbulb-on', + rightIconSize: 24, + rightIconSizeUnit: 'px', + rightIconColor: '#5469FF', + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.html new file mode 100644 index 0000000000..265a9c2501 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.html @@ -0,0 +1,51 @@ + + +
+
widgets.action-button.behavior
+
+
widgets.button-state.activated-state
+ +
+
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.ts new file mode 100644 index 0000000000..26f785fac9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.ts @@ -0,0 +1,70 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ValueType } from '@shared/models/constants'; +import { getTargetDeviceFromDatasources } from '@shared/models/widget-settings.models'; +import { actionButtonDefaultSettings } from '@home/components/widget/lib/button/action-button-widget.models'; + +@Component({ + selector: 'tb-action-button-widget-settings', + templateUrl: './action-button-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class ActionButtonWidgetSettingsComponent extends WidgetSettingsComponent { + + get targetDevice(): TargetDevice { + const datasources = this.widgetConfig?.config?.datasources; + return getTargetDeviceFromDatasources(datasources); + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; + } + get borderRadius(): string { + return this.widgetConfig?.config?.borderRadius; + } + + valueType = ValueType; + + actionButtonWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.actionButtonWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...actionButtonDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.actionButtonWidgetSettingsForm = this.fb.group({ + activatedState: [settings.activatedState, []], + disabledState: [settings.disabledState, []], + + appearance: [settings.appearance, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.html new file mode 100644 index 0000000000..29f3833983 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.html @@ -0,0 +1,49 @@ + + +
+
widgets.command-button.behavior
+
+
widgets.command-button.on-click
+ +
+
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.ts new file mode 100644 index 0000000000..ff34a28503 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.ts @@ -0,0 +1,68 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ValueType } from '@shared/models/constants'; +import { commandButtonDefaultSettings } from '@home/components/widget/lib/button/command-button-widget.models'; + +@Component({ + selector: 'tb-command-button-widget-settings', + templateUrl: './command-button-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class CommandButtonWidgetSettingsComponent extends WidgetSettingsComponent { + + get targetDevice(): TargetDevice { + return this.widgetConfig?.config?.targetDevice; + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; + } + get borderRadius(): string { + return this.widgetConfig?.config?.borderRadius; + } + + valueType = ValueType; + + commandButtonWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.commandButtonWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...commandButtonDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.commandButtonWidgetSettingsForm = this.fb.group({ + onClickState: [settings.onClickState, []], + disabledState: [settings.disabledState, []], + + appearance: [settings.appearance, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.html new file mode 100644 index 0000000000..b37030c884 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.html @@ -0,0 +1,142 @@ + + +
+
widgets.power-button.behavior
+
+
widgets.rpc-state.initial-state
+ +
+
+
widgets.power-button.power-on
+ +
+
+
widgets.power-button.power-off
+ +
+
+
widgets.rpc-state.disabled-state
+ +
+
+
+
widget-config.card-style
+ + + {{ powerButtonLayoutTranslationMap.get(layout) | translate }} + + +
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
+
widgets.power-button.button
+
+
{{ 'widgets.power-button.power-on-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
{{ 'widgets.power-button.power-off-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
{{ 'widgets.power-button.disabled-colors' | translate }}
+
+
+
widgets.power-button.main
+ + +
+ +
+
widgets.power-button.background
+ + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.ts new file mode 100644 index 0000000000..c1d9f98460 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.ts @@ -0,0 +1,88 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ValueType } from '@shared/models/constants'; +import { + powerButtonDefaultSettings, + powerButtonLayoutImages, + powerButtonLayouts, + powerButtonLayoutTranslations +} from '@home/components/widget/lib/rpc/power-button-widget.models'; + +@Component({ + selector: 'tb-power-button-widget-settings', + templateUrl: './power-button-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class PowerButtonWidgetSettingsComponent extends WidgetSettingsComponent { + + get targetDevice(): TargetDevice { + return this.widgetConfig?.config?.targetDevice; + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; + } + + powerButtonLayouts = powerButtonLayouts; + + powerButtonLayoutTranslationMap = powerButtonLayoutTranslations; + powerButtonLayoutImageMap = powerButtonLayoutImages; + + valueType = ValueType; + + powerButtonWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.powerButtonWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...powerButtonDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.powerButtonWidgetSettingsForm = this.fb.group({ + initialState: [settings.initialState, []], + onUpdateState: [settings.onUpdateState, []], + offUpdateState: [settings.offUpdateState, []], + disabledState: [settings.disabledState, []], + + layout: [settings.layout, []], + + mainColorOn: [settings.mainColorOn, []], + backgroundColorOn: [settings.backgroundColorOn, []], + + mainColorOff: [settings.mainColorOff, []], + backgroundColorOff: [settings.backgroundColorOff, []], + + mainColorDisabled: [settings.mainColorDisabled, []], + backgroundColorDisabled: [settings.backgroundColorDisabled, []], + + background: [settings.background, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html index de641b8808..31c8276666 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html @@ -35,7 +35,7 @@ {{ 'widgets.progress-bar.auto-scale' | translate }}
-
+
{{ 'widgets.progress-bar.value' | translate }} @@ -48,7 +48,7 @@
-
+
{{ 'widgets.progress-bar.range' | translate }}
widgets.progress-bar.min
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-state-settings-button.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html similarity index 88% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-state-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html index da24002e31..3290099ff9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-state-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html @@ -16,9 +16,9 @@ -->
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.scss similarity index 95% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.scss index 6af29b3cff..e4b6cadea7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.scss @@ -64,7 +64,7 @@ .gutter.gutter-horizontal { cursor: col-resize; - background-image: url("../../../../../../assets/split.js/grips/vertical.png"); + background-image: url("../../../../../../../../../assets/split.js/grips/vertical.png"); } .tb-split.tb-split-horizontal, diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.ts similarity index 95% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.ts index 47178013ae..27b04680b1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-editor.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component.ts @@ -15,7 +15,7 @@ /// // eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// +/// import { AfterViewInit, @@ -35,7 +35,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { combineLatest } from 'rxjs'; import { CustomActionDescriptor } from '@shared/models/widget.models'; -import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; +import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; @Component({ selector: 'tb-custom-action-pretty-editor', diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.html similarity index 98% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.html index 5145fc13ce..f9610ecbee 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.html @@ -100,6 +100,7 @@ [disableUndefinedCheck]="true" [validationArgs]="[]" [editorCompleter]="customPrettyActionEditorCompleter" + functionTitle="{{ 'widget-action.custom-pretty-function' | translate }}" helpId="widget/action/custom_pretty_action_fn"> diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts index 71bb0a4a2e..55f010cff3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action-pretty-resources-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts @@ -35,7 +35,7 @@ import { CustomActionDescriptor } from '@shared/models/widget.models'; import { Ace } from 'ace-builds'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { ResizeObserver } from '@juggle/resize-observer'; -import { CustomPrettyActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; +import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { Observable } from 'rxjs/internal/Observable'; import { forkJoin, from } from 'rxjs'; import { map, tap } from 'rxjs/operators'; diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-action.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action.models.ts similarity index 71% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-action.models.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action.models.ts index d9083bfe12..f1c3b98c74 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/custom-action.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action.models.ts @@ -17,6 +17,11 @@ import { TbEditorCompleter, TbEditorCompletions } from '@shared/models/ace/completion.models'; import { widgetContextCompletions } from '@shared/models/ace/widget-completion.models'; import { entityIdHref, entityTypeHref, serviceCompletions } from '@shared/models/ace/service-completion.models'; +import { CustomActionDescriptor, WidgetAction } from '@shared/models/widget.models'; +import { deepClone, isDefined, isUndefined } from '@core/utils'; +import customSampleJs from '!raw-loader!./custom-sample-js.raw'; +import customSampleCss from '!raw-loader!./custom-sample-css.raw'; +import customSampleHtml from '!raw-loader!./custom-sample-html.raw'; const customActionCompletions: TbEditorCompletions = { ...{ @@ -73,5 +78,24 @@ const customPrettyActionCompletions: TbEditorCompletions = { ...customActionCompletions }; +export const toCustomAction = (action: WidgetAction): CustomActionDescriptor => { + let result: CustomActionDescriptor; + if (!action || (isUndefined(action.customFunction) && isUndefined(action.customHtml) && isUndefined(action.customCss))) { + result = { + customHtml: customSampleHtml, + customCss: customSampleCss, + customFunction: customSampleJs + }; + } else { + result = { + customHtml: action.customHtml, + customCss: action.customCss, + customFunction: action.customFunction + }; + } + result.customResources = action && isDefined(action.customResources) ? deepClone(action.customResources) : []; + return result; +}; + export const CustomActionEditorCompleter = new TbEditorCompleter(customActionCompletions); export const CustomPrettyActionEditorCompleter = new TbEditorCompleter(customPrettyActionCompletions); diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-css.raw similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-sample-css.raw rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-css.raw diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-html.raw similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-sample-html.raw rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-html.raw diff --git a/ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-js.raw similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/action/custom-sample-js.raw rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-sample-js.raw diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html similarity index 55% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html index e290225f08..faad403e9b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html @@ -15,50 +15,50 @@ limitations under the License. --> -
-
widgets.rpc-state.initial-state
-
+
+
{{ panelTitle | translate }}
+
-
{{ 'widgets.rpc-state.action' | translate }}
+
{{ 'widgets.value-action.action' | translate }}
- - {{ rpcInitialStateTranslationsMap.get(action) | translate }} + + {{ getValueActionTranslationsMap.get(action) | translate }}
- - + +
-
widgets.rpc-state.value
- widgets.value-action.value
+
- +
-
{{ 'widgets.rpc-state.method' | translate }}*
+
{{ 'widgets.value-action.method' | translate }}*
warning
- +
-
{{ 'widgets.rpc-state.attribute-scope' | translate }}
+
{{ 'widgets.value-action.attribute-scope' | translate }}
@@ -71,30 +71,30 @@
-
{{ 'widgets.rpc-state.attribute-key' | translate }}*
+
{{ 'widgets.value-action.attribute-key' | translate }}*
+ [attributeScope]="getValueSettingsFormGroup.get('getAttribute').get('scope').value">
- +
-
{{ 'widgets.rpc-state.time-series-key' | translate }}*
+
{{ 'widgets.value-action.time-series-key' | translate }}*
-
+
-
widgets.rpc-state.action-result-converter
+
widgets.value-action.action-result-converter
- {{ 'widgets.rpc-state.converter-none' | translate }} - {{ 'widgets.rpc-state.converter-function' | translate }} + {{ 'widgets.value-action.converter-none' | translate }} + {{ 'widgets.value-action.converter-function' | translate }}
- -
-
widgets.rpc-state.on-when-result-is
+
+
{{ 'widgets.value-action.state-when-result-is' | translate:{state: (stateLabel | translate)} }}
-
@@ -142,16 +142,16 @@
-
{{ 'widgets.rpc-state.request-timeout-ms' | translate }}
+
{{ 'widgets.value-action.request-timeout-ms' | translate }}
warning @@ -159,28 +159,28 @@
+ [expanded]="getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value" + [disabled]="!getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value"> - {{ 'widgets.rpc-state.request-persistent' | translate }} + {{ 'widgets.value-action.request-persistent' | translate }}
-
{{ 'widgets.rpc-state.persistent-polling-interval' | translate }}
+
{{ 'widgets.value-action.persistent-polling-interval' | translate }}
warning @@ -193,7 +193,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts new file mode 100644 index 0000000000..f9202b982f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.ts @@ -0,0 +1,193 @@ +/// +/// Copyright © 2016-2024 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge } from 'rxjs'; +import { + DataToValueType, + GetValueAction, + getValueActionsByWidgetType, + getValueActionTranslations, + GetValueSettings +} from '@shared/models/action-widget-settings.models'; +import { ValueType } from '@shared/models/constants'; +import { TargetDevice, widgetType } from '@shared/models/widget.models'; +import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; +import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetService } from '@core/http/widget.service'; + +@Component({ + selector: 'tb-get-value-action-settings-panel', + templateUrl: './get-value-action-settings-panel.component.html', + providers: [], + styleUrls: ['./action-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class GetValueActionSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + getValueSettings: GetValueSettings; + + @Input() + panelTitle: string; + + @Input() + valueType: ValueType; + + @Input() + trueLabel = 'value.true'; + + @Input() + falseLabel = 'value.false'; + + @Input() + stateLabel: string; + + @Input() + aliasController: IAliasController; + + @Input() + targetDevice: TargetDevice; + + @Input() + widgetType: widgetType; + + @Input() + popover: TbPopoverComponent; + + @Output() + getValueSettingsApplied = new EventEmitter>(); + + getValueAction = GetValueAction; + + getValueActions: GetValueAction[]; + + getValueActionTranslationsMap = getValueActionTranslations; + + telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; + + attributeScopes = Object.keys(AttributeScope) as AttributeScope[]; + + dataKeyType = DataKeyType; + + dataToValueType = DataToValueType; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + ValueType = ValueType; + + getValueSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.getValueActions = getValueActionsByWidgetType(this.widgetType); + this.getValueSettingsFormGroup = this.fb.group( + { + action: [this.getValueSettings?.action, []], + defaultValue: [this.getValueSettings?.defaultValue, [Validators.required]], + executeRpc: this.fb.group({ + method: [this.getValueSettings?.executeRpc?.method, [Validators.required]], + requestTimeout: [this.getValueSettings?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], + requestPersistent: [this.getValueSettings?.executeRpc?.requestPersistent, []], + persistentPollingInterval: + [this.getValueSettings?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] + }), + getAttribute: this.fb.group({ + scope: [this.getValueSettings?.getAttribute?.scope, []], + key: [this.getValueSettings?.getAttribute?.key, [Validators.required]] + }), + getTimeSeries: this.fb.group({ + key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]] + }), + dataToValue: this.fb.group({ + type: [this.getValueSettings?.dataToValue?.type, [Validators.required]], + dataToValueFunction: [this.getValueSettings?.dataToValue?.dataToValueFunction, [Validators.required]], + }), + } + ); + if (this.valueType === ValueType.BOOLEAN) { + (this.getValueSettingsFormGroup.get('dataToValue') as UntypedFormGroup).addControl( + 'compareToValue', this.fb.control(this.getValueSettings?.dataToValue?.compareToValue, [Validators.required]) + ); + } + + merge(this.getValueSettingsFormGroup.get('action').valueChanges, + this.getValueSettingsFormGroup.get('dataToValue').get('type').valueChanges, + this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { + this.updateValidators(); + }); + this.updateValidators(); + } + + cancel() { + this.popover?.hide(); + } + + applyGetValueSettings() { + const getValueSettings: GetValueSettings = this.getValueSettingsFormGroup.getRawValue(); + this.getValueSettingsApplied.emit(getValueSettings); + } + + private updateValidators() { + const action: GetValueAction = this.getValueSettingsFormGroup.get('action').value; + const dataToValueType: DataToValueType = this.getValueSettingsFormGroup.get('dataToValue').get('type').value; + + this.getValueSettingsFormGroup.get('defaultValue').disable({emitEvent: false}); + this.getValueSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); + this.getValueSettingsFormGroup.get('getAttribute').disable({emitEvent: false}); + this.getValueSettingsFormGroup.get('getTimeSeries').disable({emitEvent: false}); + switch (action) { + case GetValueAction.DO_NOTHING: + this.getValueSettingsFormGroup.get('defaultValue').enable({emitEvent: false}); + break; + case GetValueAction.EXECUTE_RPC: + this.getValueSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); + const requestPersistent: boolean = this.getValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value; + if (requestPersistent) { + this.getValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); + } else { + this.getValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); + } + break; + case GetValueAction.GET_ATTRIBUTE: + this.getValueSettingsFormGroup.get('getAttribute').enable({emitEvent: false}); + break; + case GetValueAction.GET_TIME_SERIES: + this.getValueSettingsFormGroup.get('getTimeSeries').enable({emitEvent: false}); + break; + } + if (action === GetValueAction.DO_NOTHING) { + this.getValueSettingsFormGroup.get('dataToValue').disable({emitEvent: false}); + } else { + this.getValueSettingsFormGroup.get('dataToValue').enable({emitEvent: false}); + if (dataToValueType === DataToValueType.FUNCTION) { + this.getValueSettingsFormGroup.get('dataToValue').get('dataToValueFunction').enable({emitEvent: false}); + } else { + this.getValueSettingsFormGroup.get('dataToValue').get('dataToValueFunction').disable({emitEvent: false}); + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts similarity index 54% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts index 61c053d43c..5ca5bf3b84 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings.component.ts @@ -28,35 +28,47 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; -import { RpcInitialStateAction, RpcInitialStateSettings } from '@shared/models/rpc-widget-settings.models'; +import { GetValueAction, GetValueSettings } from '@shared/models/action-widget-settings.models'; import { TranslateService } from '@ngx-translate/core'; import { ValueType } from '@shared/models/constants'; import { - RpcInitialStateSettingsPanelComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component'; + GetValueActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component'; import { IAliasController } from '@core/api/widget-api.models'; -import { TargetDevice } from '@shared/models/widget.models'; +import { TargetDevice, widgetType } from '@shared/models/widget.models'; @Component({ - selector: 'tb-rpc-initial-state-settings', - templateUrl: './rpc-state-settings-button.component.html', - styleUrls: ['./rpc-state-settings-button.scss'], + selector: 'tb-get-value-action-settings', + templateUrl: './action-settings-button.component.html', + styleUrls: ['./action-settings-button.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => RpcInitialStateSettingsComponent), + useExisting: forwardRef(() => GetValueActionSettingsComponent), multi: true } ], encapsulation: ViewEncapsulation.None }) -export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAccessor { +export class GetValueActionSettingsComponent implements OnInit, ControlValueAccessor { @HostBinding('style.overflow') overflow = 'hidden'; @Input() - stateValueType: ValueType; + panelTitle: string; + + @Input() + valueType: ValueType; + + @Input() + trueLabel = 'value.true'; + + @Input() + falseLabel = 'value.false'; + + @Input() + stateLabel: string; @Input() aliasController: IAliasController; @@ -64,10 +76,13 @@ export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAcc @Input() targetDevice: TargetDevice; + @Input() + widgetType: widgetType; + @Input() disabled = false; - modelValue: RpcInitialStateSettings; + modelValue: GetValueSettings; displayValue: string; @@ -95,12 +110,12 @@ export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAcc } } - writeValue(value: RpcInitialStateSettings): void { + writeValue(value: GetValueSettings): void { this.modelValue = value; this.updateDisplayValue(); } - openRpcStateSettingsPopup($event: Event, matButton: MatButton) { + openActionSettingsPopup($event: Event, matButton: MatButton) { if ($event) { $event.stopPropagation(); } @@ -109,21 +124,26 @@ export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAcc this.popoverService.hidePopover(trigger); } else { const ctx: any = { - initialState: this.modelValue, - stateValueType: this.stateValueType, + getValueSettings: this.modelValue, + panelTitle: this.panelTitle, + valueType: this.valueType, + trueLabel: this.trueLabel, + falseLabel: this.falseLabel, + stateLabel: this.stateLabel, aliasController: this.aliasController, - targetDevice: this.targetDevice + targetDevice: this.targetDevice, + widgetType: this.widgetType }; - const initialStateSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, RpcInitialStateSettingsPanelComponent, + const getValueSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, GetValueActionSettingsPanelComponent, ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, ctx, {}, {}, {}, true); - initialStateSettingsPanelPopover.tbComponentRef.instance.popover = initialStateSettingsPanelPopover; - initialStateSettingsPanelPopover.tbComponentRef.instance.initialStateSettingsApplied.subscribe((initialState) => { - initialStateSettingsPanelPopover.hide(); - this.modelValue = initialState; + getValueSettingsPanelPopover.tbComponentRef.instance.popover = getValueSettingsPanelPopover; + getValueSettingsPanelPopover.tbComponentRef.instance.getValueSettingsApplied.subscribe((getValueSettings) => { + getValueSettingsPanelPopover.hide(); + this.modelValue = getValueSettings; this.updateDisplayValue(); this.propagateChange(this.modelValue); }); @@ -132,22 +152,23 @@ export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAcc private updateDisplayValue() { switch (this.modelValue.action) { - case RpcInitialStateAction.DO_NOTHING: - if (this.stateValueType === ValueType.BOOLEAN) { - this.displayValue = this.translate.instant(!!this.modelValue.defaultValue ? 'widgets.rpc-state.on' : 'widgets.rpc-state.off'); + case GetValueAction.DO_NOTHING: + if (this.valueType === ValueType.BOOLEAN) { + this.displayValue = + this.translate.instant(!!this.modelValue.defaultValue ? this.trueLabel : this.falseLabel); } else { this.displayValue = this.modelValue.defaultValue + ''; } break; - case RpcInitialStateAction.EXECUTE_RPC: + case GetValueAction.EXECUTE_RPC: const methodName = this.modelValue.executeRpc.method; - this.displayValue = this.translate.instant('widgets.rpc-state.execute-rpc-text', {methodName}); + this.displayValue = this.translate.instant('widgets.value-action.execute-rpc-text', {methodName}); break; - case RpcInitialStateAction.GET_ATTRIBUTE: - this.displayValue = this.translate.instant('widgets.rpc-state.get-attribute-text', {key: this.modelValue.getAttribute.key}); + case GetValueAction.GET_ATTRIBUTE: + this.displayValue = this.translate.instant('widgets.value-action.get-attribute-text', {key: this.modelValue.getAttribute.key}); break; - case RpcInitialStateAction.GET_TIME_SERIES: - this.displayValue = this.translate.instant('widgets.rpc-state.get-time-series-text', {key: this.modelValue.getTimeSeries.key}); + case GetValueAction.GET_TIME_SERIES: + this.displayValue = this.translate.instant('widgets.value-action.get-time-series-text', {key: this.modelValue.getTimeSeries.key}); break; } this.cd.markForCheck(); diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html similarity index 74% rename from ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html index 4338061a2a..458e619185 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.html @@ -15,19 +15,27 @@ limitations under the License. --> -
- - widget-action.mobile.action-type - - - {{ mobileActionTypeTranslations.get(mobileActionType[actionType]) | translate }} - - - - {{ 'widget-action.mobile.action-type-required' | translate }} - - -
+
+
+
{{ 'widget-action.mobile.action-type' | translate }}*
+ + + + {{ mobileActionTypeTranslations.get(mobileActionType[actionType]) | translate }} + + + + warning + + +
+ @@ -37,6 +45,7 @@ [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_get_location_fn" > @@ -47,6 +56,7 @@ [functionArgs]="['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_get_phone_number_fn" > @@ -60,6 +70,7 @@ [functionArgs]="['imageUrl', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_process_image_fn" > @@ -70,6 +81,7 @@ [functionArgs]="['code', 'format', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_process_qr_code_fn" > @@ -80,6 +92,7 @@ [functionArgs]="['latitude', 'longitude', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_process_location_fn" > @@ -93,24 +106,27 @@ [functionArgs]="['launched', '$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', 'entityLabel']" [globalVariables]="functionScopeVariables" [editorCompleter]="customActionEditorCompleter" + hideBrackets helpId="widget/action/mobile_process_launch_result_fn" > -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts similarity index 91% rename from ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts index af028ac0ae..694ce67758 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.component.ts @@ -14,18 +14,22 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit, QueryList, ViewChildren } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@app/core/core.state'; +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { + WidgetActionType, WidgetMobileActionDescriptor, WidgetMobileActionType, widgetMobileActionTypeTranslationMap } from '@shared/models/widget.models'; -import { CustomActionEditorCompleter } from '@home/components/widget/action/custom-action.models'; -import { JsFuncComponent } from '@shared/components/js-func.component'; +import { CustomActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { getDefaultGetLocationFunction, getDefaultGetPhoneNumberFunction, @@ -35,7 +39,7 @@ import { getDefaultProcessLaunchResultFunction, getDefaultProcessLocationFunction, getDefaultProcessQrCodeFunction -} from '@home/components/widget/action/mobile-action-editor.models'; +} from '@home/components/widget/lib/settings/common/action/mobile-action-editor.models'; import { WidgetService } from '@core/http/widget.service'; @Component({ @@ -50,8 +54,6 @@ import { WidgetService } from '@core/http/widget.service'; }) export class MobileActionEditorComponent implements ControlValueAccessor, OnInit { - @ViewChildren(JsFuncComponent) jsFuncComponents: QueryList; - mobileActionTypes = Object.keys(WidgetMobileActionType); mobileActionTypeTranslations = widgetMobileActionTypeTranslationMap; mobileActionType = WidgetMobileActionType; @@ -75,10 +77,9 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit @Input() disabled: boolean; - private propagateChange = (v: any) => { }; + private propagateChange = (_v: any) => { }; - constructor(private store: Store, - private fb: UntypedFormBuilder, + constructor(private fb: UntypedFormBuilder, private widgetService: WidgetService) { this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); } @@ -87,7 +88,7 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } ngOnInit() { @@ -158,7 +159,7 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit } this.mobileActionTypeFormGroup = this.fb.group({}); if (type) { - let processLaunchResultFunction; + let processLaunchResultFunction: string; switch (type) { case WidgetMobileActionType.takePictureFromGallery: case WidgetMobileActionType.takePhoto: @@ -253,9 +254,5 @@ export class MobileActionEditorComponent implements ControlValueAccessor, OnInit }); } - public validateOnSubmit() { - for (const jsFuncComponent of this.jsFuncComponents.toArray()) { - jsFuncComponent.validateOnSubmit(); - } - } + protected readonly WidgetActionType = WidgetActionType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts similarity index 92% rename from ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts index 2a7270a14d..5abf69c523 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/mobile-action-editor.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/mobile-action-editor.models.ts @@ -24,6 +24,7 @@ const processImageFunctionTemplate = '\n' + 'function showImageDialog(title, imageUrl) {\n' + ' setTimeout(function() {\n' + + // eslint-disable-next-line max-len ' widgetContext.customDialog.customDialog(imageDialogTemplate, ImageDialogController, {imageUrl: imageUrl, title: title}).subscribe();\n' + ' }, 100);\n' + '}\n' + @@ -82,6 +83,7 @@ const processImageFunctionTemplate = '}\n'; const processLaunchResultFunctionTemplate = + // eslint-disable-next-line max-len '// Optional function body to process result of attempt to launch external mobile application (for ex. map application or phone call application). \n' + '// - launched - boolean value indicating if the external application was successfully launched.\n\n' + 'showLaunchStatusDialog(\'--TITLE--\', launched);\n' + @@ -166,6 +168,7 @@ const getLocationFunctionTemplate = '\n' + 'function getLocationFromEntityAttributes() {\n' + ' if (entityId) {\n' + + // eslint-disable-next-line max-len ' return widgetContext.attributeService.getEntityAttributes(entityId, \'SERVER_SCOPE\', [\'latitude\', \'longitude\']).pipe(widgetContext.rxjs.map(function(attributeData) {\n' + ' var res = [0,0];\n' + ' if (attributeData && attributeData.length === 2) {\n' + @@ -188,6 +191,7 @@ const getPhoneNumberFunctionTemplate = '\n' + 'function getPhoneNumberFromEntityAttributes() {\n' + ' if (entityId) {\n' + + // eslint-disable-next-line max-len ' return widgetContext.attributeService.getEntityAttributes(entityId, \'SERVER_SCOPE\', [\'phone\']).pipe(widgetContext.rxjs.map(function(attributeData) {\n' + ' var res = 0;\n' + ' if (attributeData && attributeData.length === 1) {\n' + @@ -200,8 +204,8 @@ const getPhoneNumberFunctionTemplate = ' }\n' + '}\n'; -export function getDefaultProcessImageFunction(type: WidgetMobileActionType): string { - let title; +export const getDefaultProcessImageFunction = (type: WidgetMobileActionType): string => { + let title: string; switch (type) { case WidgetMobileActionType.takePictureFromGallery: title = 'Gallery picture'; @@ -214,10 +218,10 @@ export function getDefaultProcessImageFunction(type: WidgetMobileActionType): st break; } return processImageFunctionTemplate.replace('--TITLE--', title); -} +}; -export function getDefaultProcessLaunchResultFunction(type: WidgetMobileActionType): string { - let title; +export const getDefaultProcessLaunchResultFunction = (type: WidgetMobileActionType): string => { + let title: string; switch (type) { case WidgetMobileActionType.mapLocation: title = 'Map location'; @@ -230,25 +234,17 @@ export function getDefaultProcessLaunchResultFunction(type: WidgetMobileActionTy break; } return processLaunchResultFunctionTemplate.replace('--TITLE--', title); -} +}; -export function getDefaultProcessQrCodeFunction() { - return processQrCodeFunction; -} +export const getDefaultProcessQrCodeFunction = () => processQrCodeFunction; -export function getDefaultProcessLocationFunction() { - return processLocationFunction; -} +export const getDefaultProcessLocationFunction = () => processLocationFunction; -export function getDefaultGetLocationFunction() { - return getLocationFunctionTemplate; -} +export const getDefaultGetLocationFunction = () => getLocationFunctionTemplate; -export function getDefaultGetPhoneNumberFunction() { - return getPhoneNumberFunctionTemplate; -} +export const getDefaultGetPhoneNumberFunction = () => getPhoneNumberFunctionTemplate; -export function getDefaultHandleEmptyResultFunction(type: WidgetMobileActionType): string { +export const getDefaultHandleEmptyResultFunction = (type: WidgetMobileActionType): string => { let message = 'Mobile action was cancelled!'; switch (type) { case WidgetMobileActionType.takePictureFromGallery: @@ -277,9 +273,9 @@ export function getDefaultHandleEmptyResultFunction(type: WidgetMobileActionType break; } return handleEmptyResultFunctionTemplate.replace('--MESSAGE--', message); -} +}; -export function getDefaultHandleErrorFunction(type: WidgetMobileActionType): string { +export const getDefaultHandleErrorFunction = (type: WidgetMobileActionType): string => { let title = 'Mobile action failed'; switch (type) { case WidgetMobileActionType.takePictureFromGallery: @@ -308,4 +304,4 @@ export function getDefaultHandleErrorFunction(type: WidgetMobileActionType): str break; } return handleErrorFunctionTemplate.replace('--TITLE--', title); -} +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html similarity index 55% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html index ad70579d1c..4cf5857612 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html @@ -15,41 +15,41 @@ limitations under the License. --> -
-
{{ panelTitle | translate }}
-
+
+
{{ panelTitle | translate }}
+
-
{{ 'widgets.rpc-state.action' | translate }}
+
{{ 'widgets.value-action.action' | translate }}
- - {{ rpcUpdateStateTranslationsMap.get(action) | translate }} + + {{ setValueActionTranslationsMap.get(action) | translate }}
- - + +
-
{{ 'widgets.rpc-state.method' | translate }}*
+
{{ 'widgets.value-action.method' | translate }}*
warning
- +
-
{{ 'widgets.rpc-state.attribute-scope' | translate }}
+
{{ 'widgets.value-action.attribute-scope' | translate }}
@@ -59,30 +59,30 @@
-
{{ 'widgets.rpc-state.attribute-key' | translate }}*
+
{{ 'widgets.value-action.attribute-key' | translate }}*
+ [attributeScope]="setValueSettingsFormGroup.get('setAttribute').get('scope').value">
- +
-
{{ 'widgets.rpc-state.time-series-key' | translate }}*
+
{{ 'widgets.value-action.time-series-key' | translate }}*
-
+
-
{{ (updateStateSettingsFormGroup.get('action').value === rpcUpdateStateAction.EXECUTE_RPC ? - 'widgets.rpc-state.parameters' : 'widgets.rpc-state.value') | translate }}
+
{{ (setValueSettingsFormGroup.get('action').value === setValueAction.EXECUTE_RPC ? + 'widgets.value-action.parameters' : 'widgets.value-action.value') | translate }}
- {{ 'widgets.rpc-state.converter-constant' | translate }} - {{ 'widgets.rpc-state.converter-function' | translate }} - {{ 'widgets.rpc-state.converter-none' | translate }} + {{ 'widgets.value-action.converter-value' | translate }} + {{ 'widgets.value-action.converter-constant' | translate }} + {{ 'widgets.value-action.converter-function' | translate }} + {{ 'widgets.value-action.converter-none' | translate }}
- -
-
@@ -132,16 +133,16 @@
-
{{ 'widgets.rpc-state.request-timeout-ms' | translate }}
+
{{ 'widgets.value-action.request-timeout-ms' | translate }}
warning @@ -149,28 +150,28 @@
+ [expanded]="setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value" + [disabled]="!setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value"> - {{ 'widgets.rpc-state.request-persistent' | translate }} + {{ 'widgets.value-action.request-persistent' | translate }}
-
{{ 'widgets.rpc-state.persistent-polling-interval' | translate }}
+
{{ 'widgets.value-action.persistent-polling-interval' | translate }}
warning @@ -183,7 +184,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts new file mode 100644 index 0000000000..819ed2a3fc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.ts @@ -0,0 +1,185 @@ +/// +/// Copyright © 2016-2024 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge } from 'rxjs'; +import { + SetValueAction, + setValueActionsByWidgetType, + setValueActionTranslations, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-widget-settings.models'; +import { TargetDevice, widgetType } from '@shared/models/widget.models'; +import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; +import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetService } from '@core/http/widget.service'; +import { ValueType } from '@shared/models/constants'; + +@Component({ + selector: 'tb-set-value-action-settings-panel', + templateUrl: './set-value-action-settings-panel.component.html', + providers: [], + styleUrls: ['./action-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SetValueActionSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + panelTitle: string; + + @Input() + valueType = ValueType.BOOLEAN; + + @Input() + setValueSettings: SetValueSettings; + + @Input() + aliasController: IAliasController; + + @Input() + targetDevice: TargetDevice; + + @Input() + widgetType: widgetType; + + @Input() + popover: TbPopoverComponent; + + @Output() + setValueSettingsApplied = new EventEmitter(); + + setValueAction = SetValueAction; + + setValueActions: SetValueAction[]; + + setValueActionTranslationsMap = setValueActionTranslations; + + telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; + + attributeScopes = [AttributeScope.SERVER_SCOPE, AttributeScope.SHARED_SCOPE]; + + dataKeyType = DataKeyType; + + valueToDataType = ValueToDataType; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + ValueType = ValueType; + + setValueSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.setValueActions = setValueActionsByWidgetType(this.widgetType); + this.setValueSettingsFormGroup = this.fb.group( + { + action: [this.setValueSettings?.action, []], + executeRpc: this.fb.group({ + method: [this.setValueSettings?.executeRpc?.method, [Validators.required]], + requestTimeout: [this.setValueSettings?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], + requestPersistent: [this.setValueSettings?.executeRpc?.requestPersistent, []], + persistentPollingInterval: + [this.setValueSettings?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] + }), + setAttribute: this.fb.group({ + scope: [this.setValueSettings?.setAttribute?.scope, []], + key: [this.setValueSettings?.setAttribute?.key, [Validators.required]], + }), + putTimeSeries: this.fb.group({ + key: [this.setValueSettings?.putTimeSeries?.key, [Validators.required]], + }), + valueToData: this.fb.group({ + type: [this.setValueSettings?.valueToData?.type, [Validators.required]], + constantValue: [this.setValueSettings?.valueToData?.constantValue, [Validators.required]], + valueToDataFunction: [this.setValueSettings?.valueToData?.valueToDataFunction, [Validators.required]], + }), + } + ); + + merge(this.setValueSettingsFormGroup.get('action').valueChanges, + this.setValueSettingsFormGroup.get('valueToData').get('type').valueChanges, + this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { + this.updateValidators(); + }); + this.updateValidators(); + } + + cancel() { + this.popover?.hide(); + } + + applySetValueSettings() { + const setValueSettings: SetValueSettings = this.setValueSettingsFormGroup.getRawValue(); + this.setValueSettingsApplied.emit(setValueSettings); + } + + private updateValidators() { + const action: SetValueAction = this.setValueSettingsFormGroup.get('action').value; + let valueToDataType: ValueToDataType = this.setValueSettingsFormGroup.get('valueToData').get('type').value; + + this.setValueSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); + this.setValueSettingsFormGroup.get('setAttribute').disable({emitEvent: false}); + this.setValueSettingsFormGroup.get('putTimeSeries').disable({emitEvent: false}); + switch (action) { + case SetValueAction.EXECUTE_RPC: + this.setValueSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); + const requestPersistent: boolean = this.setValueSettingsFormGroup.get('executeRpc').get('requestPersistent').value; + if (requestPersistent) { + this.setValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); + } else { + this.setValueSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); + } + break; + case SetValueAction.SET_ATTRIBUTE: + case SetValueAction.ADD_TIME_SERIES: + if (valueToDataType === ValueToDataType.NONE) { + valueToDataType = ValueToDataType.CONSTANT; + this.setValueSettingsFormGroup.get('valueToData').get('type').patchValue(valueToDataType, {emitEvent: false}); + } + if (action === SetValueAction.SET_ATTRIBUTE) { + this.setValueSettingsFormGroup.get('setAttribute').enable({emitEvent: false}); + } else { + this.setValueSettingsFormGroup.get('putTimeSeries').enable({emitEvent: false}); + } + break; + } + switch (valueToDataType) { + case ValueToDataType.CONSTANT: + this.setValueSettingsFormGroup.get('valueToData').get('constantValue').enable({emitEvent: false}); + this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').disable({emitEvent: false}); + break; + case ValueToDataType.FUNCTION: + this.setValueSettingsFormGroup.get('valueToData').get('constantValue').disable({emitEvent: false}); + this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').enable({emitEvent: false}); + break; + case ValueToDataType.NONE: + this.setValueSettingsFormGroup.get('valueToData').get('constantValue').disable({emitEvent: false}); + this.setValueSettingsFormGroup.get('valueToData').get('valueToDataFunction').disable({emitEvent: false}); + break; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts similarity index 59% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts index 8a65861783..7b4c8e0428 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts @@ -28,34 +28,30 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; -import { - RpcStateToParamsType, - RpcUpdateStateAction, - RpcUpdateStateSettings -} from '@shared/models/rpc-widget-settings.models'; +import { SetValueAction, SetValueSettings, ValueToDataType } from '@shared/models/action-widget-settings.models'; import { TranslateService } from '@ngx-translate/core'; -import { ValueType } from '@shared/models/constants'; import { IAliasController } from '@core/api/widget-api.models'; -import { TargetDevice } from '@shared/models/widget.models'; +import { TargetDevice, widgetType } from '@shared/models/widget.models'; import { isDefinedAndNotNull } from '@core/utils'; import { - RpcUpdateStateSettingsPanelComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component'; + SetValueActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component'; +import { ValueType } from '@shared/models/constants'; @Component({ - selector: 'tb-rpc-update-state-settings', - templateUrl: './rpc-state-settings-button.component.html', - styleUrls: ['./rpc-state-settings-button.scss'], + selector: 'tb-set-value-action-settings', + templateUrl: './action-settings-button.component.html', + styleUrls: ['./action-settings-button.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => RpcUpdateStateSettingsComponent), + useExisting: forwardRef(() => SetValueActionSettingsComponent), multi: true } ], encapsulation: ViewEncapsulation.None }) -export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAccessor { +export class SetValueActionSettingsComponent implements OnInit, ControlValueAccessor { @HostBinding('style.overflow') overflow = 'hidden'; @@ -64,7 +60,7 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce panelTitle: string; @Input() - stateValueType: ValueType; + valueType = ValueType.BOOLEAN; @Input() aliasController: IAliasController; @@ -72,10 +68,13 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce @Input() targetDevice: TargetDevice; + @Input() + widgetType: widgetType; + @Input() disabled = false; - modelValue: RpcUpdateStateSettings; + modelValue: SetValueSettings; displayValue: string; @@ -103,12 +102,12 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce } } - writeValue(value: RpcUpdateStateSettings): void { + writeValue(value: SetValueSettings): void { this.modelValue = value; this.updateDisplayValue(); } - openRpcStateSettingsPopup($event: Event, matButton: MatButton) { + openActionSettingsPopup($event: Event, matButton: MatButton) { if ($event) { $event.stopPropagation(); } @@ -117,22 +116,23 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce this.popoverService.hidePopover(trigger); } else { const ctx: any = { - updateState: this.modelValue, + setValueSettings: this.modelValue, panelTitle: this.panelTitle, - stateValueType: this.stateValueType, + valueType: this.valueType, aliasController: this.aliasController, - targetDevice: this.targetDevice + targetDevice: this.targetDevice, + widgetType: this.widgetType }; - const updateStateSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, RpcUpdateStateSettingsPanelComponent, + const setValueSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, SetValueActionSettingsPanelComponent, ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, ctx, {}, {}, {}, true); - updateStateSettingsPanelPopover.tbComponentRef.instance.popover = updateStateSettingsPanelPopover; - updateStateSettingsPanelPopover.tbComponentRef.instance.updateStateSettingsApplied.subscribe((updateState) => { - updateStateSettingsPanelPopover.hide(); - this.modelValue = updateState; + setValueSettingsPanelPopover.tbComponentRef.instance.popover = setValueSettingsPanelPopover; + setValueSettingsPanelPopover.tbComponentRef.instance.setValueSettingsApplied.subscribe((setValueSettings) => { + setValueSettingsPanelPopover.hide(); + this.modelValue = setValueSettings; this.updateDisplayValue(); this.propagateChange(this.modelValue); }); @@ -141,31 +141,34 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce private updateDisplayValue() { let value: any; - switch (this.modelValue.stateToParams.type) { - case RpcStateToParamsType.CONSTANT: - value = this.modelValue.stateToParams.constantValue; + switch (this.modelValue.valueToData.type) { + case ValueToDataType.VALUE: + value = 'value'; + break; + case ValueToDataType.CONSTANT: + value = this.modelValue.valueToData.constantValue; break; - case RpcStateToParamsType.FUNCTION: + case ValueToDataType.FUNCTION: value = 'f(value)'; break; - case RpcStateToParamsType.NONE: + case ValueToDataType.NONE: break; } switch (this.modelValue.action) { - case RpcUpdateStateAction.EXECUTE_RPC: + case SetValueAction.EXECUTE_RPC: let methodName = this.modelValue.executeRpc.method; if (isDefinedAndNotNull(value)) { methodName = `${methodName}(${value})`; } - this.displayValue = this.translate.instant('widgets.rpc-state.execute-rpc-text', {methodName}); + this.displayValue = this.translate.instant('widgets.value-action.execute-rpc-text', {methodName}); break; - case RpcUpdateStateAction.SET_ATTRIBUTE: - this.displayValue = this.translate.instant('widgets.rpc-state.set-attribute-to-value-text', + case SetValueAction.SET_ATTRIBUTE: + this.displayValue = this.translate.instant('widgets.value-action.set-attribute-to-value-text', {key: this.modelValue.setAttribute.key, value}); break; - case RpcUpdateStateAction.ADD_TIME_SERIES: - this.displayValue = this.translate.instant('widgets.rpc-state.add-time-series-value-text', - {key: this.modelValue.setAttribute.key, value}); + case SetValueAction.ADD_TIME_SERIES: + this.displayValue = this.translate.instant('widgets.value-action.add-time-series-value-text', + {key: this.modelValue.putTimeSeries.key, value}); break; } this.cd.markForCheck(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.html new file mode 100644 index 0000000000..c5b4b50272 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.html @@ -0,0 +1,42 @@ + +
+
{{ panelTitle | translate }}
+
+ + +
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts new file mode 100644 index 0000000000..8ed53fd95e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts @@ -0,0 +1,88 @@ +/// +/// Copyright © 2016-2024 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge } from 'rxjs'; +import { + DataToValueType, + GetValueAction, + getValueActions, + getValueActionTranslations, + GetValueSettings +} from '@shared/models/action-widget-settings.models'; +import { ValueType } from '@shared/models/constants'; +import { TargetDevice, WidgetAction, widgetType } from '@shared/models/widget.models'; +import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; +import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetService } from '@core/http/widget.service'; +import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; + +@Component({ + selector: 'tb-widget-action-settings-panel', + templateUrl: './widget-action-settings-panel.component.html', + providers: [], + styleUrls: ['./action-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class WidgetActionSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + widgetAction: WidgetAction; + + @Input() + panelTitle: string; + + @Input() + widgetType: widgetType; + + @Input() + callbacks: WidgetActionCallbacks; + + @Input() + popover: TbPopoverComponent; + + @Output() + widgetActionApplied = new EventEmitter(); + + widgetActionFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.widgetActionFormGroup = this.fb.group( + { + widgetAction: [this.widgetAction, []] + } + ); + } + + cancel() { + this.popover?.hide(); + } + + applyWidgetAction() { + const widgetAction: WidgetAction = this.widgetActionFormGroup.get('widgetAction').getRawValue(); + this.widgetActionApplied.emit(widgetAction); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts new file mode 100644 index 0000000000..95ec0b1262 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts @@ -0,0 +1,136 @@ +/// +/// Copyright © 2016-2024 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 { + ChangeDetectorRef, + Component, + forwardRef, + HostBinding, + Input, + OnInit, + Renderer2, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { TranslateService } from '@ngx-translate/core'; +import { WidgetAction, widgetActionTypeTranslationMap, widgetType } from '@shared/models/widget.models'; +import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { + WidgetActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/widget-action-settings-panel.component'; + +@Component({ + selector: 'tb-widget-action-settings', + templateUrl: './action-settings-button.component.html', + styleUrls: ['./action-settings-button.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetActionSettingsComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class WidgetActionSettingsComponent implements OnInit, ControlValueAccessor { + + @HostBinding('style.overflow') + overflow = 'hidden'; + + @Input() + panelTitle: string; + + @Input() + widgetType: widgetType; + + @Input() + callbacks: WidgetActionCallbacks; + + @Input() + disabled = false; + + modelValue: WidgetAction; + + displayValue: string; + + private propagateChange = null; + + constructor(private translate: TranslateService, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + if (this.disabled !== isDisabled) { + this.disabled = isDisabled; + } + } + + writeValue(value: WidgetAction): void { + this.modelValue = value; + this.updateDisplayValue(); + } + + openActionSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + widgetAction: this.modelValue, + panelTitle: this.panelTitle, + widgetType: this.widgetType, + callbacks: this.callbacks + }; + const widgetActionSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, WidgetActionSettingsPanelComponent, + ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + widgetActionSettingsPanelPopover.tbComponentRef.instance.popover = widgetActionSettingsPanelPopover; + widgetActionSettingsPanelPopover.tbComponentRef.instance.widgetActionApplied.subscribe((widgetAction) => { + widgetActionSettingsPanelPopover.hide(); + this.modelValue = widgetAction; + this.updateDisplayValue(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateDisplayValue() { + this.displayValue = this.translate.instant(widgetActionTypeTranslationMap.get(this.modelValue.type)); + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html new file mode 100644 index 0000000000..3752fb3b3c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html @@ -0,0 +1,251 @@ + +
+
+
widget-config.action
+ + + + {{ widgetActionTypeTranslations.get(widgetActionType[actionType]) | translate }} + + + +
+ + +
+
{{ 'widget-action.target-dashboard' | translate }}*
+ +
+
+ +
+
{{ 'widget-action.target-dashboard-state' | translate }} + {{widgetActionFormGroup.get('type').value === widgetActionType.openDashboardState ? '*' : ''}}
+ + + + + warning + + + + + + + +
+
+ +
+
{{ 'widget-action.URL' | translate }}
+ + + + warning + + +
+
+ +
+ + {{ 'widget-action.open-right-layout' | translate }} + +
+
+ +
+ + {{ 'widget-action.open-new-browser-tab' | translate }} + +
+
+ +
+ + {{ 'widget-action.set-entity-from-widget' | translate }} + +
+
+
{{ 'alias.state-entity-parameter-name' | translate }}
+ + + +
+
+ +
+
{{ 'widget-action.state-display-type' | translate }}
+ + + + {{ stateDisplayTypeName(displayType) }} + + + +
+ + +
+
{{ 'widget-action.dialog-title' | translate }}
+ + + +
+
+ + {{ 'widget-action.dialog-hide-dashboard-toolbar' | translate }} + +
+
+
{{ 'widget-action.dialog-width' | translate }}
+ + + + warning + + +
+
+
{{ 'widget-action.dialog-height' | translate }}
+ + + + warning + + +
+
+ +
+
{{ 'widget-action.popover-preferred-placement' | translate }}
+ + + + {{ popoverPlacementName(placement) }} + + + +
+
+ + {{ 'widget-action.popover-hide-on-click-outside' | translate }} + +
+
+ + {{ 'widget-action.popover-hide-dashboard-toolbar' | translate }} + +
+
+
{{ 'widget-action.popover-width' | translate }}
+ + +
+
+
{{ 'widget-action.popover-height' | translate }}
+ + +
+ +
+ + +
+
+ + + + + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts new file mode 100644 index 0000000000..6c2cd3fb8f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts @@ -0,0 +1,473 @@ +/// +/// Copyright © 2016-2024 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 { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { + WidgetAction, + WidgetActionType, + widgetActionTypeTranslationMap, + widgetType +} from '@shared/models/widget.models'; +import { WidgetService } from '@core/http/widget.service'; +import { WidgetActionCallbacks } from '@home/components/widget/action/manage-widget-actions.component.models'; +import { map, mergeMap, share, startWith, takeUntil, tap } from 'rxjs/operators'; +import { Observable, of, Subject, Subscription } from 'rxjs'; +import { Dashboard } from '@shared/models/dashboard.models'; +import { DashboardService } from '@core/http/dashboard.service'; +import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; +import { isDefinedAndNotNull } from '@core/utils'; +import { TranslateService } from '@ngx-translate/core'; +import { PopoverPlacement, PopoverPlacements } from '@shared/components/popover.models'; +import { + CustomActionEditorCompleter, + toCustomAction +} from '@home/components/widget/lib/settings/common/action/custom-action.models'; + +const stateDisplayTypes = ['normal', 'separateDialog', 'popover'] as const; +type stateDisplayTypeTuple = typeof stateDisplayTypes; +export type stateDisplayType = stateDisplayTypeTuple[number]; + +const stateDisplayTypesTranslations = new Map( + [ + ['normal', 'widget-action.open-normal'], + ['separateDialog', 'widget-action.open-in-separate-dialog'], + ['popover', 'widget-action.open-in-popover'], + ] +); + +@Component({ + selector: 'tb-widget-action', + templateUrl: './widget-action.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetActionComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => WidgetActionComponent), + multi: true, + } + ] +}) +export class WidgetActionComponent implements ControlValueAccessor, OnInit, Validator { + + @ViewChild('dashboardStateInput', {static: false}) dashboardStateInput: ElementRef; + + @Input() + disabled: boolean; + + @Input() + widgetType: widgetType; + + @Input() + callbacks: WidgetActionCallbacks; + + widgetActionTypes = Object.keys(WidgetActionType); + widgetActionTypeTranslations = widgetActionTypeTranslationMap; + widgetActionType = WidgetActionType; + + allStateDisplayTypes = stateDisplayTypes; + allPopoverPlacements = PopoverPlacements; + + WidgetType = widgetType; + + filteredDashboardStates: Observable>; + targetDashboardStateSearchText = ''; + selectedDashboardStateIds: Observable>; + + customActionEditorCompleter = CustomActionEditorCompleter; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + widgetActionFormGroup: UntypedFormGroup; + actionTypeFormGroup: UntypedFormGroup; + stateDisplayTypeFormGroup: UntypedFormGroup; + + private propagateChange = (_val: any) => {}; + private actionTypeFormGroupSubscriptions: Subscription[] = []; + private stateDisplayTypeFormGroupSubscriptions: Subscription[] = []; + private destroy$ = new Subject(); + private dashboard: Dashboard; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + private dashboardService: DashboardService, + private dashboardUtils: DashboardUtilsService, + private translate: TranslateService) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.widgetActionFormGroup.disable({emitEvent: false}); + if (this.actionTypeFormGroup) { + this.actionTypeFormGroup.disable({emitEvent: false}); + } + if (this.stateDisplayTypeFormGroup) { + this.stateDisplayTypeFormGroup.disable({emitEvent: false}); + } + } else { + this.widgetActionFormGroup.enable({emitEvent: false}); + } + } + + ngOnInit() { + this.widgetActionFormGroup = this.fb.group({}); + this.widgetActionFormGroup.addControl('type', + this.fb.control(null, [Validators.required])); + this.widgetActionFormGroup.get('type').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((type: WidgetActionType) => { + this.updateActionTypeFormGroup(type); + }); + this.widgetActionFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { + this.widgetActionUpdated(); + }); + } + + writeValue(widgetAction?: WidgetAction): void { + this.widgetActionFormGroup.patchValue({ + type: widgetAction?.type + }, {emitEvent: false}); + this.updateActionTypeFormGroup(widgetAction?.type, widgetAction); + } + + validate(_c: UntypedFormControl) { + return (this.widgetActionFormGroup.valid && + this.actionTypeFormGroup.valid && (!this.stateDisplayTypeFormGroup || this.stateDisplayTypeFormGroup.valid)) ? null : { + widgetAction: { + valid: false, + } + }; + } + + clearTargetDashboardState(value: string = '') { + this.dashboardStateInput.nativeElement.value = value; + this.actionTypeFormGroup.get('targetDashboardStateId').patchValue(value, {emitEvent: true}); + setTimeout(() => { + this.dashboardStateInput.nativeElement.blur(); + this.dashboardStateInput.nativeElement.focus(); + }, 0); + } + + onDashboardStateInputFocus(): void { + this.actionTypeFormGroup.get('targetDashboardStateId').updateValueAndValidity({onlySelf: true, emitEvent: true}); + } + + stateDisplayTypeName(displayType: stateDisplayType): string { + if (displayType) { + return this.translate.instant(stateDisplayTypesTranslations.get(displayType)) + ''; + } else { + return ''; + } + } + + popoverPlacementName(placement: PopoverPlacement): string { + if (placement) { + return this.translate.instant(`widget-action.popover-placement-${placement}`) + ''; + } else { + return ''; + } + } + + private updateActionTypeFormGroup(type?: WidgetActionType, action?: WidgetAction) { + this.actionTypeFormGroupSubscriptions.forEach(s => s.unsubscribe()); + this.actionTypeFormGroupSubscriptions.length = 0; + this.actionTypeFormGroup = this.fb.group({}); + if (type) { + switch (type) { + case WidgetActionType.openDashboard: + case WidgetActionType.openDashboardState: + case WidgetActionType.updateDashboardState: + this.actionTypeFormGroup.addControl( + 'targetDashboardStateId', + this.fb.control(action ? action.targetDashboardStateId : null, + type === WidgetActionType.openDashboardState ? [Validators.required] : []) + ); + this.actionTypeFormGroup.addControl( + 'setEntityId', + this.fb.control(this.widgetType === widgetType.static ? false : action ? action.setEntityId : true, []) + ); + this.actionTypeFormGroup.addControl( + 'stateEntityParamName', + this.fb.control(action ? action.stateEntityParamName : null, []) + ); + if (type === WidgetActionType.openDashboard) { + this.actionTypeFormGroup.addControl( + 'openNewBrowserTab', + this.fb.control(action ? action.openNewBrowserTab : false, []) + ); + this.actionTypeFormGroup.addControl( + 'targetDashboardId', + this.fb.control(action ? action.targetDashboardId : null, + [Validators.required]) + ); + this.setupSelectedDashboardStateIds(); + } else { + if (type === WidgetActionType.openDashboardState) { + const displayType = this.getStateDisplayType(action); + this.actionTypeFormGroup.addControl( + 'stateDisplayType', + this.fb.control(this.getStateDisplayType(action), [Validators.required]) + ); + this.updateStateDisplayTypeFormGroup(displayType, action); + this.actionTypeFormGroupSubscriptions.push( + this.actionTypeFormGroup.get('stateDisplayType').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((displayTypeValue: stateDisplayType) => { + this.updateStateDisplayTypeFormGroup(displayTypeValue); + }) + ); + } + this.actionTypeFormGroup.addControl( + 'openRightLayout', + this.fb.control(action ? action.openRightLayout : false, []) + ); + } + this.setupFilteredDashboardStates(); + break; + case WidgetActionType.custom: + this.actionTypeFormGroup.addControl( + 'customFunction', + this.fb.control(action ? action.customFunction : null, []) + ); + break; + case WidgetActionType.customPretty: + this.actionTypeFormGroup.addControl( + 'customAction', + this.fb.control(toCustomAction(action), [Validators.required]) + ); + break; + case WidgetActionType.mobileAction: + this.actionTypeFormGroup.addControl( + 'mobileAction', + this.fb.control(action ? action.mobileAction : null, [Validators.required]) + ); + break; + case WidgetActionType.openURL: + this.actionTypeFormGroup.addControl( + 'openNewBrowserTab', + this.fb.control(action ? action.openNewBrowserTab : false, []) + ); + this.actionTypeFormGroup.addControl( + 'url', + this.fb.control(action ? action.url : null, [Validators.required]) + ); + break; + } + } + this.actionTypeFormGroupSubscriptions.push( + this.actionTypeFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { + this.widgetActionUpdated(); + }) + ); + } + + private updateStateDisplayTypeFormGroup(displayType?: stateDisplayType, action?: WidgetAction) { + this.stateDisplayTypeFormGroupSubscriptions.forEach(s => s.unsubscribe()); + this.stateDisplayTypeFormGroupSubscriptions.length = 0; + this.stateDisplayTypeFormGroup = this.fb.group({}); + if (displayType) { + switch (displayType) { + case 'normal': + break; + case 'separateDialog': + this.stateDisplayTypeFormGroup.addControl( + 'dialogTitle', + this.fb.control(action ? action.dialogTitle : '', []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'dialogHideDashboardToolbar', + this.fb.control(action && isDefinedAndNotNull(action.dialogHideDashboardToolbar) + ? action.dialogHideDashboardToolbar : true, []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'dialogWidth', + this.fb.control(action ? action.dialogWidth : null, [Validators.min(1), Validators.max(100)]) + ); + this.stateDisplayTypeFormGroup.addControl( + 'dialogHeight', + this.fb.control(action ? action.dialogHeight : null, [Validators.min(1), Validators.max(100)]) + ); + break; + case 'popover': + this.stateDisplayTypeFormGroup.addControl( + 'popoverPreferredPlacement', + this.fb.control(action && isDefinedAndNotNull(action.popoverPreferredPlacement) + ? action.popoverPreferredPlacement : 'top', []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'popoverHideOnClickOutside', + this.fb.control(action && isDefinedAndNotNull(action.popoverHideOnClickOutside) + ? action.popoverHideOnClickOutside : true, []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'popoverHideDashboardToolbar', + this.fb.control(action && isDefinedAndNotNull(action.popoverHideDashboardToolbar) + ? action.popoverHideDashboardToolbar : true, []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'popoverWidth', + this.fb.control(action && isDefinedAndNotNull(action.popoverWidth) ? action.popoverWidth : '25vw', []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'popoverHeight', + this.fb.control(action && isDefinedAndNotNull(action.popoverHeight) ? action.popoverHeight : '25vh', []) + ); + this.stateDisplayTypeFormGroup.addControl( + 'popoverStyle', + this.fb.control(action && isDefinedAndNotNull(action.popoverStyle) ? action.popoverStyle : {}, []) + ); + break; + } + } + this.stateDisplayTypeFormGroupSubscriptions.push( + this.stateDisplayTypeFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { + this.widgetActionUpdated(); + }) + ); + } + + private setupSelectedDashboardStateIds() { + this.selectedDashboardStateIds = + this.actionTypeFormGroup.get('targetDashboardId').valueChanges.pipe( + tap((dashboardId) => { + if (!dashboardId) { + this.actionTypeFormGroup.get('targetDashboardStateId') + .patchValue('', {emitEvent: true}); + } + + this.targetDashboardStateSearchText = ''; + }), + mergeMap((dashboardId) => { + if (dashboardId) { + if (this.dashboard?.id.id === dashboardId) { + return of(this.dashboard); + } else { + return this.dashboardService.getDashboard(dashboardId); + } + } else { + return of(null); + } + }), + map((dashboard: Dashboard) => { + if (dashboard) { + if (this.dashboard?.id.id !== dashboard.id.id) { + this.dashboard = this.dashboardUtils.validateAndUpdateDashboard(dashboard); + } + + return Object.keys(this.dashboard.configuration.states); + } else { + return []; + } + }), + share() + ); + } + + private setupFilteredDashboardStates() { + this.targetDashboardStateSearchText = ''; + this.filteredDashboardStates = this.actionTypeFormGroup.get('targetDashboardStateId').valueChanges + .pipe( + startWith(''), + map(value => value ? value : ''), + mergeMap(name => this.fetchDashboardStates(name)), + takeUntil(this.destroy$) + ); + } + + private fetchDashboardStates(searchText?: string): Observable> { + this.targetDashboardStateSearchText = searchText; + if (this.widgetActionFormGroup.get('type').value === WidgetActionType.openDashboard) { + return this.selectedDashboardStateIds.pipe( + map(stateIds => { + const result = searchText ? stateIds.filter(this.createFilterForDashboardState(searchText)) : stateIds; + if (result && result.length) { + return result; + } else { + return [searchText]; + } + }) + ); + } else { + return of(this.callbacks.fetchDashboardStates(searchText)); + } + } + + private createFilterForDashboardState(query: string): (stateId: string) => boolean { + const lowercaseQuery = query.toLowerCase(); + return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0; + } + + private getStateDisplayType(action?: WidgetAction): stateDisplayType { + let res: stateDisplayType = 'normal'; + if (action) { + if (action.openInSeparateDialog) { + res = 'separateDialog'; + } else if (action.openInPopover) { + res = 'popover'; + } + } + return res; + } + + private widgetActionUpdated() { + const type: WidgetActionType = this.widgetActionFormGroup.get('type').value; + let result: WidgetAction; + if (type === WidgetActionType.customPretty) { + result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.get('customAction').value}; + } else { + result = {...this.widgetActionFormGroup.value, ...this.actionTypeFormGroup.value}; + } + if (this.actionTypeFormGroup.get('stateDisplayType') && + this.actionTypeFormGroup.get('stateDisplayType').value !== 'normal') { + result = {...result, ...this.stateDisplayTypeFormGroup.value}; + result.openInSeparateDialog = this.actionTypeFormGroup.get('stateDisplayType').value === 'separateDialog'; + result.openInPopover = this.actionTypeFormGroup.get('stateDisplayType').value === 'popover'; + } else { + result.openInSeparateDialog = false; + result.openInPopover = false; + } + delete (result as any).stateDisplayType; + this.propagateChange(result); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html new file mode 100644 index 0000000000..bdecc9d36b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html @@ -0,0 +1,97 @@ + +
+ + + {{ widgetButtonTypeTranslationMap.get(type) | translate }} + + +
+ + {{ 'widgets.button.auto-scale' | translate }} + +
+
+ + {{ 'widgets.button.label' | translate }} + + + + +
+
+ + {{ 'widgets.button.icon' | translate }} + +
+ + + + + + +
+
+
+
{{ 'widgets.button.color-palette' | translate }}
+
+
+
widgets.button.main
+ + +
+ +
+
widgets.button.background
+ + +
+
+
+
+ + + +
widgets.button.custom-styles
+
+
+ +
+
{{ widgetButtonStateTranslationMap.get(state) | translate }}
+ + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts new file mode 100644 index 0000000000..d8756e3e31 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts @@ -0,0 +1,141 @@ +/// +/// Copyright © 2016-2024 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 { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { + WidgetButtonAppearance, + widgetButtonStates, widgetButtonStatesTranslations, + widgetButtonTypeImages, + widgetButtonTypes, + widgetButtonTypeTranslations +} from '@shared/components/button/widget-button.models'; +import { merge } from 'rxjs'; + +@Component({ + selector: 'tb-widget-button-appearance', + templateUrl: './widget-button-appearance.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetButtonAppearanceComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonAppearanceComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled = false; + + @Input() + borderRadius: string; + + widgetButtonTypes = widgetButtonTypes; + + widgetButtonTypeTranslationMap = widgetButtonTypeTranslations; + widgetButtonTypeImageMap = widgetButtonTypeImages; + + widgetButtonStates = widgetButtonStates; + widgetButtonStateTranslationMap = widgetButtonStatesTranslations; + + modelValue: WidgetButtonAppearance; + + appearanceFormGroup: UntypedFormGroup; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder) {} + + ngOnInit(): void { + this.appearanceFormGroup = this.fb.group({ + type: [null, []], + autoScale: [null, []], + showLabel: [null, []], + label: [null, []], + showIcon: [null, []], + icon: [null, []], + iconSize: [null, []], + iconSizeUnit: [null, []], + mainColor: [null, []], + backgroundColor: [null, []] + }); + const customStyle = this.fb.group({}); + for (const state of widgetButtonStates) { + customStyle.addControl(state, this.fb.control(null, [])); + } + this.appearanceFormGroup.addControl('customStyle', customStyle); + this.appearanceFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + merge(this.appearanceFormGroup.get('showLabel').valueChanges, + this.appearanceFormGroup.get('showIcon').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.appearanceFormGroup.disable({emitEvent: false}); + } else { + this.appearanceFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: WidgetButtonAppearance): void { + this.modelValue = value; + this.appearanceFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateModel() { + this.modelValue = this.appearanceFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } + + private updateValidators(): void { + const showLabel: boolean = this.appearanceFormGroup.get('showLabel').value; + const showIcon: boolean = this.appearanceFormGroup.get('showIcon').value; + if (showLabel) { + this.appearanceFormGroup.get('label').enable(); + } else { + this.appearanceFormGroup.get('label').disable(); + } + if (showIcon) { + this.appearanceFormGroup.get('icon').enable(); + this.appearanceFormGroup.get('iconSize').enable(); + this.appearanceFormGroup.get('iconSizeUnit').enable(); + } else { + this.appearanceFormGroup.get('icon').disable(); + this.appearanceFormGroup.get('iconSize').disable(); + this.appearanceFormGroup.get('iconSizeUnit').disable(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.html new file mode 100644 index 0000000000..e5750fd4bb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.html @@ -0,0 +1,94 @@ + +
+
{{ widgetButtonStateTranslationMap.get(state) | translate }}
+
+
+ + {{ 'widgets.button.main' | translate }} + + + +
+
+ + {{ 'widgets.button.background' | translate }} + + + +
+
+ + {{ 'widgets.button.shadow' | translate }} + + + {{ 'widgets.button.enabled' | translate }} + {{ 'widgets.button.disabled' | translate }} + +
+
+
+ widgets.button.preview +
+ + +
+
+
+ + + +
+ +
+
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.scss new file mode 100644 index 0000000000..dfbbb91620 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.scss @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2024 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 '../../../../../../../../../scss/constants'; + +.tb-widget-button-custom-style-panel { + width: 530px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-widget-button-custom-style-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + } + .tb-widget-button-custom-style-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-widget-button-custom-style-preview { + flex: 1; + background: rgba(0, 0, 0, 0.04); + display: flex; + flex-direction: column; + padding: 12px 16px 24px 16px; + align-items: center; + gap: 12px; + .tb-widget-button-custom-style-preview-title { + align-self: stretch; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + color: rgba(0, 0, 0, 0.38); + } + tb-widget-button { + width: 200px; + height: 60px; + } + } + .tb-widget-button-custom-style-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts new file mode 100644 index 0000000000..cab7ab8b00 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts @@ -0,0 +1,195 @@ +/// +/// Copyright © 2016-2024 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 { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + defaultBackgroundColorDisabled, + defaultMainColorDisabled, + WidgetButtonAppearance, + WidgetButtonCustomStyle, + WidgetButtonState, + widgetButtonStates, + widgetButtonStatesTranslations, + WidgetButtonType +} from '@shared/components/button/widget-button.models'; +import { merge } from 'rxjs'; +import { deepClone } from '@core/utils'; +import { WidgetButtonComponent } from '@shared/components/button/widget-button.component'; + +@Component({ + selector: 'tb-widget-button-custom-style-panel', + templateUrl: './widget-button-custom-style-panel.component.html', + providers: [], + styleUrls: ['./widget-button-custom-style-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonCustomStylePanelComponent extends PageComponent implements OnInit { + + @ViewChild('widgetButtonPreview') + widgetButtonPreview: WidgetButtonComponent; + + @Input() + appearance: WidgetButtonAppearance; + + @Input() + borderRadius: string; + + @Input() + state: WidgetButtonState; + + @Input() + customStyle: WidgetButtonCustomStyle; + + private popoverValue: TbPopoverComponent; + + @Input() + set popover(popover: TbPopoverComponent) { + this.popoverValue = popover; + popover.tbAnimationDone.subscribe(() => { + this.widgetButtonPreview?.validateSize(); + }); + } + + get popover(): TbPopoverComponent { + return this.popoverValue; + } + + @Output() + customStyleApplied = new EventEmitter(); + + widgetButtonStateTranslationMap = widgetButtonStatesTranslations; + + widgetButtonState = WidgetButtonState; + + previewAppearance: WidgetButtonAppearance; + + copyFromStates: WidgetButtonState[]; + + customStyleFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + protected store: Store, + private cd: ChangeDetectorRef) { + super(store); + } + + ngOnInit(): void { + this.copyFromStates = widgetButtonStates.filter(state => + state !== this.state && !!this.appearance.customStyle[state]); + this.customStyleFormGroup = this.fb.group( + { + overrideMainColor: [false, []], + mainColor: [null, []], + overrideBackgroundColor: [false, []], + backgroundColor: [null, []], + overrideDropShadow: [false, []], + dropShadow: [false, []] + } + ); + merge(this.customStyleFormGroup.get('overrideMainColor').valueChanges, + this.customStyleFormGroup.get('overrideBackgroundColor').valueChanges, + this.customStyleFormGroup.get('overrideDropShadow').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + this.customStyleFormGroup.valueChanges.subscribe(() => { + this.updatePreviewAppearance(); + }); + this.setStyle(this.customStyle); + } + + copyStyle(state: WidgetButtonState) { + this.customStyle = deepClone(this.appearance.customStyle[state]); + this.setStyle(this.customStyle); + this.customStyleFormGroup.markAsDirty(); + } + + cancel() { + this.popover?.hide(); + } + + applyCustomStyle() { + const customStyle: WidgetButtonCustomStyle = this.customStyleFormGroup.value; + this.customStyleApplied.emit(customStyle); + } + + private setStyle(customStyle?: WidgetButtonCustomStyle): void { + let mainColor = this.state === WidgetButtonState.disabled ? defaultMainColorDisabled : this.appearance.mainColor; + if (customStyle?.overrideMainColor) { + mainColor = customStyle?.mainColor; + } + let backgroundColor = this.state === WidgetButtonState.disabled ? defaultBackgroundColorDisabled : this.appearance.backgroundColor; + if (customStyle?.overrideBackgroundColor) { + backgroundColor = customStyle?.backgroundColor; + } + let dropShadow = this.appearance.type !== WidgetButtonType.basic; + if (customStyle?.overrideDropShadow) { + dropShadow = customStyle?.dropShadow; + } + this.customStyleFormGroup.patchValue({ + overrideMainColor: customStyle?.overrideMainColor, + mainColor, + overrideBackgroundColor: customStyle?.overrideBackgroundColor, + backgroundColor, + overrideDropShadow: customStyle?.overrideDropShadow, + dropShadow + }, {emitEvent: false}); + this.updateValidators(); + this.updatePreviewAppearance(); + } + + private updateValidators() { + const overrideMainColor: boolean = this.customStyleFormGroup.get('overrideMainColor').value; + const overrideBackgroundColor: boolean = this.customStyleFormGroup.get('overrideBackgroundColor').value; + const overrideDropShadow: boolean = this.customStyleFormGroup.get('overrideDropShadow').value; + + if (overrideMainColor) { + this.customStyleFormGroup.get('mainColor').enable({emitEvent: false}); + } else { + this.customStyleFormGroup.get('mainColor').disable({emitEvent: false}); + } + if (overrideBackgroundColor) { + this.customStyleFormGroup.get('backgroundColor').enable({emitEvent: false}); + } else { + this.customStyleFormGroup.get('backgroundColor').disable({emitEvent: false}); + } + if (overrideDropShadow) { + this.customStyleFormGroup.get('dropShadow').enable({emitEvent: false}); + } else { + this.customStyleFormGroup.get('dropShadow').disable({emitEvent: false}); + } + } + + private updatePreviewAppearance() { + this.previewAppearance = deepClone(this.appearance); + this.previewAppearance.customStyle[this.state] = this.customStyleFormGroup.value; + this.cd.markForCheck(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.html new file mode 100644 index 0000000000..3b78af6d5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.html @@ -0,0 +1,44 @@ + +
+
+ + + +
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.scss new file mode 100644 index 0000000000..d7cdeffec5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.scss @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2024 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 '../../../../../../../../../scss/constants'; + +.tb-widget-button-custom-style { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + button.mat-mdc-icon-button { + color: rgba(0,0,0,0.56); + } + .tb-widget-button-preview-panel { + width: 148px; + height: 48px; + padding: 8px 12px; + border-radius: 4px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + tb-widget-button { + width: 84px; + height: 100%; + } + @media #{$mat-gt-xs} { + width: 168px; + tb-widget-button { + width: 104px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts new file mode 100644 index 0000000000..a67f9a45ab --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts @@ -0,0 +1,158 @@ +/// +/// Copyright © 2016-2024 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 { + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnChanges, + OnInit, + Renderer2, + SimpleChanges, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { + WidgetButtonAppearance, + WidgetButtonCustomStyle, + WidgetButtonState +} from '@shared/components/button/widget-button.models'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MatIconButton } from '@angular/material/button'; +import { + WidgetButtonCustomStylePanelComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component'; +import { deepClone } from '@core/utils'; + +@Component({ + selector: 'tb-widget-button-custom-style', + templateUrl: './widget-button-custom-style.component.html', + styleUrls: ['./widget-button-custom-style.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetButtonCustomStyleComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonCustomStyleComponent implements OnInit, OnChanges, ControlValueAccessor { + + @Input() + disabled = false; + + @Input() + appearance: WidgetButtonAppearance; + + @Input() + borderRadius: string; + + @Input() + state: WidgetButtonState; + + widgetButtonState = WidgetButtonState; + + modelValue: WidgetButtonCustomStyle; + + previewAppearance: WidgetButtonAppearance; + + private propagateChange = (_val: any) => {}; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef) {} + + ngOnInit(): void { + this.updatePreviewAppearance(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange) { + if (propName === 'appearance') { + this.updatePreviewAppearance(); + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(_isDisabled: boolean): void { + } + + writeValue(value: WidgetButtonCustomStyle): void { + this.modelValue = value; + this.updatePreviewAppearance(); + } + + clearStyle() { + this.updateModel(null); + } + + openButtonCustomStylePopup($event: Event, matButton: MatIconButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + appearance: this.appearance, + borderRadius: this.borderRadius, + state: this.state, + customStyle: this.modelValue + }; + const widgetButtonCustomStylePanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, WidgetButtonCustomStylePanelComponent, + ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + widgetButtonCustomStylePanelPopover.tbComponentRef.instance.popover = widgetButtonCustomStylePanelPopover; + widgetButtonCustomStylePanelPopover.tbComponentRef.instance.customStyleApplied.subscribe((customStyle) => { + widgetButtonCustomStylePanelPopover.hide(); + this.updateModel(customStyle); + }); + } + } + + private updateModel(value: WidgetButtonCustomStyle): void { + this.modelValue = value; + this.updatePreviewAppearance(); + this.propagateChange(this.modelValue); + } + + private updatePreviewAppearance() { + this.previewAppearance = deepClone(this.appearance); + if (this.modelValue) { + this.previewAppearance.customStyle[this.state] = this.modelValue; + } + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.html new file mode 100644 index 0000000000..31836d433d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.html @@ -0,0 +1,33 @@ + +
+ + + + warning + + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts new file mode 100644 index 0000000000..dcd579e071 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts @@ -0,0 +1,124 @@ +/// +/// Copyright © 2016-2024 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 { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator, + Validators +} from '@angular/forms'; +import { cssUnit, resolveCssSize } from '@shared/models/widget-settings.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-css-size-input', + templateUrl: './css-size-input.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CssSizeInputComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => CssSizeInputComponent), + multi: true, + } + ] +}) +export class CssSizeInputComponent implements OnInit, ControlValueAccessor, Validator { + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input() + requiredText: string; + + @Input() + @coerceBoolean() + allowEmptyUnit = false; + + cssSizeFormGroup: UntypedFormGroup; + + modelValue: string; + + private propagateChange = null; + + constructor(private fb: UntypedFormBuilder) {} + + ngOnInit(): void { + this.cssSizeFormGroup = this.fb.group({ + size: [null, this.required ? [Validators.required, Validators.min(0)] : [Validators.min(0)]], + unit: [null, []] + }); + this.cssSizeFormGroup.valueChanges.subscribe((value: {size: number; unit: cssUnit}) => { + this.updateModel(value); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.cssSizeFormGroup.disable({emitEvent: false}); + } else { + this.cssSizeFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: string): void { + this.modelValue = value; + const size = resolveCssSize(value); + this.cssSizeFormGroup.patchValue({ + size: size[0], + unit: size[1] + }, {emitEvent: false}); + } + + validate(_c: UntypedFormControl) { + return this.cssSizeFormGroup.valid ? null : { + cssSize: { + valid: false, + } + }; + } + + private updateModel(value: {size: number; unit: cssUnit}): void { + const result: string = isDefinedAndNotNull(value?.size) && isDefinedAndNotNull(value?.unit) + ? value.size + value.unit : ''; + if (this.modelValue !== result) { + this.modelValue = result; + this.propagateChange(this.modelValue); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html index 8455c9b2cf..095310606f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html @@ -15,7 +15,7 @@ limitations under the License. --> - + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts index 8c78d26a39..588871b7b9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts @@ -40,6 +40,9 @@ export class CssUnitSelectComponent implements OnInit, ControlValueAccessor { @coerceBoolean() allowEmpty = false; + @Input() + width = '100%'; + cssUnitsList = cssUnits; cssUnitFormControl: UntypedFormControl; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.ts deleted file mode 100644 index b9f5f9b602..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component.ts +++ /dev/null @@ -1,176 +0,0 @@ -/// -/// Copyright © 2016-2024 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { TbPopoverComponent } from '@shared/components/popover.component'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { merge } from 'rxjs'; -import { - RpcDataToStateType, - RpcInitialStateAction, - rpcInitialStateActions, - RpcInitialStateSettings, - rpcInitialStateTranslations -} from '@shared/models/rpc-widget-settings.models'; -import { ValueType } from '@shared/models/constants'; -import { TargetDevice } from '@shared/models/widget.models'; -import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; -import { IAliasController } from '@core/api/widget-api.models'; -import { WidgetService } from '@core/http/widget.service'; - -@Component({ - selector: 'tb-rpc-initial-state-settings-panel', - templateUrl: './rpc-initial-state-settings-panel.component.html', - providers: [], - styleUrls: ['./rpc-state-settings-panel.component.scss'], - encapsulation: ViewEncapsulation.None -}) -export class RpcInitialStateSettingsPanelComponent extends PageComponent implements OnInit { - - @Input() - initialState: RpcInitialStateSettings; - - @Input() - stateValueType: ValueType; - - @Input() - aliasController: IAliasController; - - @Input() - targetDevice: TargetDevice; - - @Input() - popover: TbPopoverComponent; - - @Output() - initialStateSettingsApplied = new EventEmitter>(); - - rpcInitialStateAction = RpcInitialStateAction; - - rpcInitialStateActions = rpcInitialStateActions; - - rpcInitialStateTranslationsMap = rpcInitialStateTranslations; - - telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; - - attributeScopes = Object.keys(AttributeScope) as AttributeScope[]; - - dataKeyType = DataKeyType; - - dataToStateType = RpcDataToStateType; - - functionScopeVariables = this.widgetService.getWidgetScopeVariables(); - - valueType = ValueType; - - initialStateSettingsFormGroup: UntypedFormGroup; - - constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService, - protected store: Store) { - super(store); - } - - ngOnInit(): void { - this.initialStateSettingsFormGroup = this.fb.group( - { - action: [this.initialState?.action, []], - defaultValue: [this.initialState?.defaultValue, [Validators.required]], - executeRpc: this.fb.group({ - method: [this.initialState?.executeRpc?.method, [Validators.required]], - requestTimeout: [this.initialState?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], - requestPersistent: [this.initialState?.executeRpc?.requestPersistent, []], - persistentPollingInterval: [this.initialState?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] - }), - getAttribute: this.fb.group({ - scope: [this.initialState?.getAttribute?.scope, []], - key: [this.initialState?.getAttribute?.key, [Validators.required]], - }), - getTimeSeries: this.fb.group({ - key: [this.initialState?.getTimeSeries?.key, [Validators.required]], - }), - dataToState: this.fb.group({ - type: [this.initialState?.dataToState?.type, [Validators.required]], - dataToStateFunction: [this.initialState?.dataToState?.dataToStateFunction, [Validators.required]], - }), - } - ); - if (this.stateValueType === ValueType.BOOLEAN) { - (this.initialStateSettingsFormGroup.get('dataToState') as UntypedFormGroup).addControl( - 'compareToValue', this.fb.control(this.initialState?.dataToState?.compareToValue, [Validators.required]) - ); - } - - merge(this.initialStateSettingsFormGroup.get('action').valueChanges, - this.initialStateSettingsFormGroup.get('dataToState').get('type').valueChanges, - this.initialStateSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { - this.updateValidators(); - }); - this.updateValidators(); - } - - cancel() { - this.popover?.hide(); - } - - applyInitialStateSettings() { - const initialStateSettings: RpcInitialStateSettings = this.initialStateSettingsFormGroup.getRawValue(); - this.initialStateSettingsApplied.emit(initialStateSettings); - } - - private updateValidators() { - const action: RpcInitialStateAction = this.initialStateSettingsFormGroup.get('action').value; - const dataToStateType: RpcDataToStateType = this.initialStateSettingsFormGroup.get('dataToState').get('type').value; - - this.initialStateSettingsFormGroup.get('defaultValue').disable({emitEvent: false}); - this.initialStateSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); - this.initialStateSettingsFormGroup.get('getAttribute').disable({emitEvent: false}); - this.initialStateSettingsFormGroup.get('getTimeSeries').disable({emitEvent: false}); - switch (action) { - case RpcInitialStateAction.DO_NOTHING: - this.initialStateSettingsFormGroup.get('defaultValue').enable({emitEvent: false}); - break; - case RpcInitialStateAction.EXECUTE_RPC: - this.initialStateSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); - const requestPersistent: boolean = this.initialStateSettingsFormGroup.get('executeRpc').get('requestPersistent').value; - if (requestPersistent) { - this.initialStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); - } else { - this.initialStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); - } - break; - case RpcInitialStateAction.GET_ATTRIBUTE: - this.initialStateSettingsFormGroup.get('getAttribute').enable({emitEvent: false}); - break; - case RpcInitialStateAction.GET_TIME_SERIES: - this.initialStateSettingsFormGroup.get('getTimeSeries').enable({emitEvent: false}); - break; - } - if (action === RpcInitialStateAction.DO_NOTHING) { - this.initialStateSettingsFormGroup.get('dataToState').disable({emitEvent: false}); - } else { - this.initialStateSettingsFormGroup.get('dataToState').enable({emitEvent: false}); - if (dataToStateType === RpcDataToStateType.FUNCTION) { - this.initialStateSettingsFormGroup.get('dataToState').get('dataToStateFunction').enable({emitEvent: false}); - } else { - this.initialStateSettingsFormGroup.get('dataToState').get('dataToStateFunction').disable({emitEvent: false}); - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.ts deleted file mode 100644 index 78c5c6f44b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component.ts +++ /dev/null @@ -1,180 +0,0 @@ -/// -/// Copyright © 2016-2024 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 { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { TbPopoverComponent } from '@shared/components/popover.component'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { merge } from 'rxjs'; -import { - RpcStateToParamsType, - RpcUpdateStateAction, - rpcUpdateStateActions, - RpcUpdateStateSettings, - rpcUpdateStateTranslations -} from '@shared/models/rpc-widget-settings.models'; -import { ValueType } from '@shared/models/constants'; -import { TargetDevice } from '@shared/models/widget.models'; -import { AttributeScope, DataKeyType, telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; -import { IAliasController } from '@core/api/widget-api.models'; -import { WidgetService } from '@core/http/widget.service'; - -@Component({ - selector: 'tb-rpc-update-state-settings-panel', - templateUrl: './rpc-update-state-settings-panel.component.html', - providers: [], - styleUrls: ['./rpc-state-settings-panel.component.scss'], - encapsulation: ViewEncapsulation.None -}) -export class RpcUpdateStateSettingsPanelComponent extends PageComponent implements OnInit { - - @Input() - panelTitle: string; - - @Input() - updateState: RpcUpdateStateSettings; - - @Input() - stateValueType: ValueType; - - @Input() - aliasController: IAliasController; - - @Input() - targetDevice: TargetDevice; - - @Input() - popover: TbPopoverComponent; - - @Output() - updateStateSettingsApplied = new EventEmitter(); - - rpcUpdateStateAction = RpcUpdateStateAction; - - rpcUpdateStateActions = rpcUpdateStateActions; - - rpcUpdateStateTranslationsMap = rpcUpdateStateTranslations; - - telemetryTypeTranslationsMap = telemetryTypeTranslationsShort; - - attributeScopes = [AttributeScope.SERVER_SCOPE, AttributeScope.SHARED_SCOPE]; - - dataKeyType = DataKeyType; - - stateToParamsType = RpcStateToParamsType; - - functionScopeVariables = this.widgetService.getWidgetScopeVariables(); - - valueType = ValueType; - - updateStateSettingsFormGroup: UntypedFormGroup; - - constructor(private fb: UntypedFormBuilder, - private widgetService: WidgetService, - protected store: Store) { - super(store); - } - - ngOnInit(): void { - this.updateStateSettingsFormGroup = this.fb.group( - { - action: [this.updateState?.action, []], - executeRpc: this.fb.group({ - method: [this.updateState?.executeRpc?.method, [Validators.required]], - requestTimeout: [this.updateState?.executeRpc?.requestTimeout, [Validators.required, Validators.min(5000)]], - requestPersistent: [this.updateState?.executeRpc?.requestPersistent, []], - persistentPollingInterval: [this.updateState?.executeRpc?.persistentPollingInterval, [Validators.required, Validators.min(1000)]] - }), - setAttribute: this.fb.group({ - scope: [this.updateState?.setAttribute?.scope, []], - key: [this.updateState?.setAttribute?.key, [Validators.required]], - }), - putTimeSeries: this.fb.group({ - key: [this.updateState?.putTimeSeries?.key, [Validators.required]], - }), - stateToParams: this.fb.group({ - type: [this.updateState?.stateToParams?.type, [Validators.required]], - constantValue: [this.updateState?.stateToParams?.constantValue, [Validators.required]], - stateToParamsFunction: [this.updateState?.stateToParams?.stateToParamsFunction, [Validators.required]], - }), - } - ); - - merge(this.updateStateSettingsFormGroup.get('action').valueChanges, - this.updateStateSettingsFormGroup.get('stateToParams').get('type').valueChanges, - this.updateStateSettingsFormGroup.get('executeRpc').get('requestPersistent').valueChanges).subscribe(() => { - this.updateValidators(); - }); - this.updateValidators(); - } - - cancel() { - this.popover?.hide(); - } - - applyUpdateStateSettings() { - const updateStateSettings: RpcUpdateStateSettings = this.updateStateSettingsFormGroup.getRawValue(); - this.updateStateSettingsApplied.emit(updateStateSettings); - } - - private updateValidators() { - const action: RpcUpdateStateAction = this.updateStateSettingsFormGroup.get('action').value; - let stateToParamsType: RpcStateToParamsType = this.updateStateSettingsFormGroup.get('stateToParams').get('type').value; - - this.updateStateSettingsFormGroup.get('executeRpc').disable({emitEvent: false}); - this.updateStateSettingsFormGroup.get('setAttribute').disable({emitEvent: false}); - this.updateStateSettingsFormGroup.get('putTimeSeries').disable({emitEvent: false}); - switch (action) { - case RpcUpdateStateAction.EXECUTE_RPC: - this.updateStateSettingsFormGroup.get('executeRpc').enable({emitEvent: false}); - const requestPersistent: boolean = this.updateStateSettingsFormGroup.get('executeRpc').get('requestPersistent').value; - if (requestPersistent) { - this.updateStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').enable({emitEvent: false}); - } else { - this.updateStateSettingsFormGroup.get('executeRpc').get('persistentPollingInterval').disable({emitEvent: false}); - } - break; - case RpcUpdateStateAction.SET_ATTRIBUTE: - case RpcUpdateStateAction.ADD_TIME_SERIES: - if (stateToParamsType === RpcStateToParamsType.NONE) { - stateToParamsType = RpcStateToParamsType.CONSTANT; - this.updateStateSettingsFormGroup.get('stateToParams').get('type').patchValue(stateToParamsType, {emitEvent: false}); - } - if (action === RpcUpdateStateAction.SET_ATTRIBUTE) { - this.updateStateSettingsFormGroup.get('setAttribute').enable({emitEvent: false}); - } else { - this.updateStateSettingsFormGroup.get('putTimeSeries').enable({emitEvent: false}); - } - break; - } - switch (stateToParamsType) { - case RpcStateToParamsType.CONSTANT: - this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').enable({emitEvent: false}); - this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').disable({emitEvent: false}); - break; - case RpcStateToParamsType.FUNCTION: - this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').disable({emitEvent: false}); - this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').enable({emitEvent: false}); - break; - case RpcStateToParamsType.NONE: - this.updateStateSettingsFormGroup.get('stateToParams').get('constantValue').disable({emitEvent: false}); - this.updateStateSettingsFormGroup.get('stateToParams').get('stateToParamsFunction').disable({emitEvent: false}); - break; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index ba08a39bb7..e39c079f75 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -52,20 +52,46 @@ import { ColorRangeSettingsComponent, ColorRangeSettingsComponentService } from '@home/components/widget/lib/settings/common/color-range-settings.component'; import { - RpcInitialStateSettingsComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings.component'; + GetValueActionSettingsComponent +} from '@home/components/widget/lib/settings/common/action/get-value-action-settings.component'; import { - RpcInitialStateSettingsPanelComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-initial-state-settings-panel.component'; + GetValueActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component'; import { DeviceKeyAutocompleteComponent } from '@home/components/widget/lib/settings/control/device-key-autocomplete.component'; import { - RpcUpdateStateSettingsComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-update-state-settings.component'; + SetValueActionSettingsComponent +} from '@home/components/widget/lib/settings/common/action/set-value-action-settings.component'; import { - RpcUpdateStateSettingsPanelComponent -} from '@home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component'; + SetValueActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component'; +import { CssSizeInputComponent } from '@home/components/widget/lib/settings/common/css-size-input.component'; +import { WidgetActionComponent } from '@home/components/widget/lib/settings/common/action/widget-action.component'; +import { + CustomActionPrettyResourcesTabsComponent +} from '@home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component'; +import { + CustomActionPrettyEditorComponent +} from '@home/components/widget/lib/settings/common/action/custom-action-pretty-editor.component'; +import { + MobileActionEditorComponent +} from '@home/components/widget/lib/settings/common/action/mobile-action-editor.component'; +import { + WidgetActionSettingsComponent +} from '@home/components/widget/lib/settings/common/action/widget-action-settings.component'; +import { + WidgetActionSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/action/widget-action-settings-panel.component'; +import { + WidgetButtonAppearanceComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-appearance.component'; +import { + WidgetButtonCustomStyleComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-custom-style.component'; +import { + WidgetButtonCustomStylePanelComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component'; @NgModule({ declarations: [ @@ -76,6 +102,7 @@ import { ColorSettingsComponent, ColorSettingsPanelComponent, CssUnitSelectComponent, + CssSizeInputComponent, DateFormatSelectComponent, DateFormatSettingsPanelComponent, BackgroundSettingsComponent, @@ -87,11 +114,20 @@ import { ColorRangeListComponent, ColorRangePanelComponent, ColorRangeSettingsComponent, - RpcInitialStateSettingsComponent, - RpcInitialStateSettingsPanelComponent, + GetValueActionSettingsComponent, + GetValueActionSettingsPanelComponent, DeviceKeyAutocompleteComponent, - RpcUpdateStateSettingsComponent, - RpcUpdateStateSettingsPanelComponent + SetValueActionSettingsComponent, + SetValueActionSettingsPanelComponent, + WidgetActionComponent, + CustomActionPrettyResourcesTabsComponent, + CustomActionPrettyEditorComponent, + MobileActionEditorComponent, + WidgetActionSettingsComponent, + WidgetActionSettingsPanelComponent, + WidgetButtonAppearanceComponent, + WidgetButtonCustomStyleComponent, + WidgetButtonCustomStylePanelComponent ], imports: [ CommonModule, @@ -106,6 +142,7 @@ import { ColorSettingsComponent, ColorSettingsPanelComponent, CssUnitSelectComponent, + CssSizeInputComponent, DateFormatSelectComponent, DateFormatSettingsPanelComponent, BackgroundSettingsComponent, @@ -117,11 +154,20 @@ import { ColorRangeListComponent, ColorRangePanelComponent, ColorRangeSettingsComponent, - RpcInitialStateSettingsComponent, - RpcInitialStateSettingsPanelComponent, + GetValueActionSettingsComponent, + GetValueActionSettingsPanelComponent, DeviceKeyAutocompleteComponent, - RpcUpdateStateSettingsComponent, - RpcUpdateStateSettingsPanelComponent + SetValueActionSettingsComponent, + SetValueActionSettingsPanelComponent, + WidgetActionComponent, + CustomActionPrettyResourcesTabsComponent, + CustomActionPrettyEditorComponent, + MobileActionEditorComponent, + WidgetActionSettingsComponent, + WidgetActionSettingsPanelComponent, + WidgetButtonAppearanceComponent, + WidgetButtonCustomStyleComponent, + WidgetButtonCustomStylePanelComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/led-indicator-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/led-indicator-widget-settings.component.ts index 22af44f2b4..70f4846b78 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/led-indicator-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/led-indicator-widget-settings.component.ts @@ -32,7 +32,7 @@ export class LedIndicatorWidgetSettingsComponent extends WidgetSettingsComponent functionScopeVariables = this.widgetService.getWidgetScopeVariables(); get targetDevice(): TargetDevice { - return this.widget?.config?.targetDevice; + return this.widgetConfig?.config?.targetDevice; } dataKeyType = DataKeyType; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/round-switch-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/round-switch-widget-settings.component.ts index 8bf58a9b22..a8d4d70015 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/round-switch-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/round-switch-widget-settings.component.ts @@ -37,7 +37,7 @@ export class RoundSwitchWidgetSettingsComponent extends WidgetSettingsComponent } get targetDevice(): TargetDevice { - return this.widget?.config?.targetDevice; + return this.widgetConfig?.config?.targetDevice; } protected settingsForm(): UntypedFormGroup { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html index 1006d65f55..9208efd68a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.html @@ -20,29 +20,45 @@
widgets.single-switch.behavior
widgets.rpc-state.initial-state
- +
widgets.rpc-state.turn-on
- + [widgetType]="widgetType" + formControlName="onUpdateState">
widgets.rpc-state.turn-off
- + [widgetType]="widgetType" + formControlName="offUpdateState"> +
+
+
widgets.rpc-state.disabled-state
+
@@ -65,7 +81,7 @@ {{ 'widgets.single-switch.auto-scale' | translate }}
-
+
{{ 'widgets.single-switch.label' | translate }} @@ -82,7 +98,7 @@
-
+
{{ 'widgets.single-switch.icon' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts index 7e4f1d1d96..a1a3e951f2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts @@ -15,12 +15,13 @@ /// import { Component } from '@angular/core'; -import { TargetDevice, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { - singleSwitchDefaultSettings, singleSwitchLayoutImages, + singleSwitchDefaultSettings, + singleSwitchLayoutImages, singleSwitchLayouts, singleSwitchLayoutTranslations } from '@home/components/widget/lib/rpc/single-switch-widget.models'; @@ -34,7 +35,11 @@ import { ValueType } from '@shared/models/constants'; export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent { get targetDevice(): TargetDevice { - return this.widget?.config?.targetDevice; + return this.widgetConfig?.config?.targetDevice; + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; } singleSwitchLayouts = singleSwitchLayouts; @@ -64,6 +69,7 @@ export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent initialState: [settings.initialState, []], onUpdateState: [settings.onUpdateState, []], offUpdateState: [settings.offUpdateState, []], + disabledState: [settings.disabledState, []], layout: [settings.layout, []], autoScale: [settings.autoScale, []], @@ -104,7 +110,7 @@ export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent return ['showLabel', 'showIcon', 'showOnLabel', 'showOffLabel']; } - protected updateValidators(emitEvent: boolean): void { + protected updateValidators(_emitEvent: boolean): void { const showLabel: boolean = this.singleSwitchWidgetSettingsForm.get('showLabel').value; const showIcon: boolean = this.singleSwitchWidgetSettingsForm.get('showIcon').value; const showOnLabel: boolean = this.singleSwitchWidgetSettingsForm.get('showOnLabel').value; @@ -152,6 +158,4 @@ export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent this.singleSwitchWidgetSettingsForm.get('offLabelColor').disable(); } } - - protected readonly ValueType = ValueType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts index d14d0fd594..43098a011b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slide-toggle-widget-settings.component.ts @@ -37,7 +37,7 @@ export class SlideToggleWidgetSettingsComponent extends WidgetSettingsComponent } get targetDevice(): TargetDevice { - return this.widget?.config?.targetDevice; + return this.widgetConfig?.config?.targetDevice; } protected settingsForm(): UntypedFormGroup { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.html new file mode 100644 index 0000000000..0f3c3f11a3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.html @@ -0,0 +1,216 @@ + + +
+
widgets.slider.behavior
+
+
widgets.slider.initial-value
+ +
+
+
widgets.slider.on-value-change
+ +
+
+
widgets.rpc-state.disabled-state
+ +
+
+
+
widget-config.card-style
+ + + {{ sliderLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.slider.auto-scale' | translate }} + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
+
widgets.slider.slider
+
+ + {{ 'widgets.slider.value' | translate }} + +
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.slider.range' | translate }}
+
+
widgets.slider.min
+ + + +
widgets.slider.max
+ + + +
+
+
+ + {{ 'widgets.slider.range-ticks' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.slider.tick-marks' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.slider.colors' | translate }}
+
+
+
widgets.slider.main
+ + +
+ +
+
widgets.slider.background
+ + +
+
+
+
+
{{ 'widgets.rpc-state.disabled-state' | translate }}
+
+
+
widgets.slider.main
+ + +
+ +
+
widgets.slider.background
+ + +
+
+
+
+
+ {{ 'widgets.slider.left-icon' | translate }} +
+
+ + + + + + + + +
+
+
+
+ {{ 'widgets.slider.right-icon' | translate }} +
+
+ + + + + + + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.ts new file mode 100644 index 0000000000..a215a93d88 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.ts @@ -0,0 +1,186 @@ +/// +/// Copyright © 2016-2024 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 { Component } from '@angular/core'; +import { TargetDevice, WidgetSettings, WidgetSettingsComponent, widgetType } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ValueType } from '@shared/models/constants'; +import { + SliderLayout, + sliderLayoutImages, + sliderLayouts, + sliderLayoutTranslations, + sliderWidgetDefaultSettings +} from '@home/components/widget/lib/rpc/slider-widget.models'; +import { formatValue } from '@core/utils'; + +@Component({ + selector: 'tb-slider-widget-settings', + templateUrl: './slider-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class SliderWidgetSettingsComponent extends WidgetSettingsComponent { + + get targetDevice(): TargetDevice { + return this.widgetConfig?.config?.targetDevice; + } + + get widgetType(): widgetType { + return this.widgetConfig?.widgetType; + } + + sliderLayout = SliderLayout; + + sliderLayouts = sliderLayouts; + + sliderLayoutTranslationMap = sliderLayoutTranslations; + sliderLayoutImageMap = sliderLayoutImages; + + valueType = ValueType; + + sliderWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.sliderWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...sliderWidgetDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.sliderWidgetSettingsForm = this.fb.group({ + initialState: [settings.initialState, []], + valueChange: [settings.valueChange, []], + disabledState: [settings.disabledState, []], + + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showValue: [settings.showValue, []], + valueUnits: [settings.valueUnits, []], + valueDecimals: [settings.valueDecimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + tickMin: [settings.tickMin, []], + tickMax: [settings.tickMax, []], + + showTicks: [settings.showTicks, []], + ticksFont: [settings.ticksFont, []], + ticksColor: [settings.ticksColor, []], + + showTickMarks: [settings.showTickMarks, []], + tickMarksCount: [settings.tickMarksCount, [Validators.min(2)]], + tickMarksColor: [settings.tickMarksColor, []], + + mainColor: [settings.mainColor, []], + backgroundColor: [settings.backgroundColor, []], + + mainColorDisabled: [settings.mainColorDisabled, []], + backgroundColorDisabled: [settings.backgroundColorDisabled, []], + + leftIconSize: [settings.leftIconSize, [Validators.min(0)]], + leftIconSizeUnit: [settings.leftIconSizeUnit, []], + leftIcon: [settings.leftIcon, []], + leftIconColor: [settings.leftIconColor, []], + + rightIconSize: [settings.rightIconSize, [Validators.min(0)]], + rightIconSizeUnit: [settings.rightIconSizeUnit, []], + rightIcon: [settings.rightIcon, []], + rightIconColor: [settings.rightIconColor, []], + + background: [settings.background, []], + }); + } + + protected validatorTriggers(): string[] { + return ['showValue', 'showTicks', 'showTickMarks', 'layout']; + } + + protected updateValidators(_emitEvent: boolean): void { + const showValue: boolean = this.sliderWidgetSettingsForm.get('showValue').value; + const showTicks: boolean = this.sliderWidgetSettingsForm.get('showTicks').value; + const showTickMarks: boolean = this.sliderWidgetSettingsForm.get('showTickMarks').value; + const layout: SliderLayout = this.sliderWidgetSettingsForm.get('layout').value; + + const valueEnabled = layout !== SliderLayout.simplified; + const leftRightIconsEnabled = layout === SliderLayout.extended; + + if (valueEnabled && showValue) { + this.sliderWidgetSettingsForm.get('valueUnits').enable(); + this.sliderWidgetSettingsForm.get('valueDecimals').enable(); + this.sliderWidgetSettingsForm.get('valueFont').enable(); + this.sliderWidgetSettingsForm.get('valueColor').enable(); + } else { + this.sliderWidgetSettingsForm.get('valueUnits').disable(); + this.sliderWidgetSettingsForm.get('valueDecimals').disable(); + this.sliderWidgetSettingsForm.get('valueFont').disable(); + this.sliderWidgetSettingsForm.get('valueColor').disable(); + } + + if (showTicks) { + this.sliderWidgetSettingsForm.get('ticksFont').enable(); + this.sliderWidgetSettingsForm.get('ticksColor').enable(); + } else { + this.sliderWidgetSettingsForm.get('ticksFont').disable(); + this.sliderWidgetSettingsForm.get('ticksColor').disable(); + } + + if (showTickMarks) { + this.sliderWidgetSettingsForm.get('tickMarksCount').enable(); + this.sliderWidgetSettingsForm.get('tickMarksColor').enable(); + } else { + this.sliderWidgetSettingsForm.get('tickMarksCount').disable(); + this.sliderWidgetSettingsForm.get('tickMarksColor').disable(); + } + + if (leftRightIconsEnabled) { + this.sliderWidgetSettingsForm.get('leftIconSize').enable(); + this.sliderWidgetSettingsForm.get('leftIconSizeUnit').enable(); + this.sliderWidgetSettingsForm.get('leftIcon').enable(); + this.sliderWidgetSettingsForm.get('leftIconColor').enable(); + this.sliderWidgetSettingsForm.get('rightIconSize').enable(); + this.sliderWidgetSettingsForm.get('rightIconSizeUnit').enable(); + this.sliderWidgetSettingsForm.get('rightIcon').enable(); + this.sliderWidgetSettingsForm.get('rightIconColor').enable(); + } else { + this.sliderWidgetSettingsForm.get('leftIconSize').disable(); + this.sliderWidgetSettingsForm.get('leftIconSizeUnit').disable(); + this.sliderWidgetSettingsForm.get('leftIcon').disable(); + this.sliderWidgetSettingsForm.get('leftIconColor').disable(); + this.sliderWidgetSettingsForm.get('rightIconSize').disable(); + this.sliderWidgetSettingsForm.get('rightIconSizeUnit').disable(); + this.sliderWidgetSettingsForm.get('rightIcon').disable(); + this.sliderWidgetSettingsForm.get('rightIconColor').disable(); + } + } + + private _valuePreviewFn(): string { + const units: string = this.sliderWidgetSettingsForm.get('valueUnits').value; + const decimals: number = this.sliderWidgetSettingsForm.get('valueDecimals').value; + return formatValue(48, decimals, units, false); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-control-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-control-widget-settings.component.ts index 197fc2d229..e74275b659 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-control-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/switch-control-widget-settings.component.ts @@ -37,7 +37,7 @@ export class SwitchControlWidgetSettingsComponent extends WidgetSettingsComponen } get targetDevice(): TargetDevice { - return this.widget?.config?.targetDevice; + return this.widgetConfig?.config?.targetDevice; } protected settingsForm(): UntypedFormGroup { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index e742475e80..0040e7fa64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html @@ -19,8 +19,8 @@
-
-
widgets.liquid-level-card.shape
+
+
widgets.liquid-level-card.shape
{{ DataSourceTypeTranslations.get(type) | translate }} @@ -51,7 +51,7 @@ formControlName="shapeAttributeName">
-
+
widgets.liquid-level-card.shape-by-attribute
-
+
widgets.liquid-level-card.widget-units
-
+
@@ -111,9 +111,9 @@
-
+
widgets.liquid-level-card.total-volume
-
+
@@ -141,10 +141,30 @@ formControlName="volumeAttributeName"> - +
+
+
widgets.liquid-level-card.total-volume-units
+
+ + + + {{ DataSourceTypeTranslations.get(type) | translate }} + + + + + + + +
@@ -168,7 +188,7 @@
widgets.liquid-level-card.value
-
+
@@ -178,7 +198,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -212,7 +232,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -232,7 +252,7 @@ {{ 'widgets.value-card.date' | translate }} -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts index 5ed3bea446..e25e02d1b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -158,7 +158,9 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon volumeSource: [settings.volumeSource, []], volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]], volumeAttributeName: [settings.volumeAttributeName, [Validators.required]], + volumeUnitsSource: [settings.volumeUnitsSource, []], volumeUnits: [settings.volumeUnits, [Validators.required]], + volumeUnitsAttributeName: [settings.volumeUnitsAttributeName, [Validators.required]], volumeFont: [settings.volumeFont, []], volumeColor: [settings.volumeColor, []], valueFont: [settings.valueFont, []], @@ -195,7 +197,7 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon protected validatorTriggers(): string[] { return [ 'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', - 'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource' + 'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource', 'volumeUnitsSource' ]; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 30c1b748de..8fc6ef96c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -15,7 +15,9 @@ /// import { NgModule, Type } from '@angular/core'; -import { QrCodeWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/qrcode-widget-settings.component'; +import { + QrCodeWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/qrcode-widget-settings.component'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; @@ -33,7 +35,9 @@ import { MarkdownWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/markdown-widget-settings.component'; import { LabelWidgetLabelComponent } from '@home/components/widget/lib/settings/cards/label-widget-label.component'; -import { LabelWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/label-widget-settings.component'; +import { + LabelWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/label-widget-settings.component'; import { SimpleCardWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/simple-card-widget-settings.component'; @@ -311,6 +315,18 @@ import { import { SingleSwitchWidgetSettingsComponent } from '@home/components/widget/lib/settings/control/single-switch-widget-settings.component'; +import { + ActionButtonWidgetSettingsComponent +} from '@home/components/widget/lib/settings/button/action-button-widget-settings.component'; +import { + CommandButtonWidgetSettingsComponent +} from '@home/components/widget/lib/settings/button/command-button-widget-settings.component'; +import { + PowerButtonWidgetSettingsComponent +} from '@home/components/widget/lib/settings/button/power-button-widget-settings.component'; +import { + SliderWidgetSettingsComponent +} from '@home/components/widget/lib/settings/control/slider-widget-settings.component'; @NgModule({ declarations: [ @@ -424,7 +440,11 @@ import { DoughnutWidgetSettingsComponent, RangeChartWidgetSettingsComponent, BarChartWithLabelsWidgetSettingsComponent, - SingleSwitchWidgetSettingsComponent + SingleSwitchWidgetSettingsComponent, + ActionButtonWidgetSettingsComponent, + CommandButtonWidgetSettingsComponent, + PowerButtonWidgetSettingsComponent, + SliderWidgetSettingsComponent ], imports: [ CommonModule, @@ -543,7 +563,11 @@ import { DoughnutWidgetSettingsComponent, RangeChartWidgetSettingsComponent, BarChartWithLabelsWidgetSettingsComponent, - SingleSwitchWidgetSettingsComponent + SingleSwitchWidgetSettingsComponent, + ActionButtonWidgetSettingsComponent, + CommandButtonWidgetSettingsComponent, + PowerButtonWidgetSettingsComponent, + SliderWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -629,5 +653,9 @@ export const widgetSettingsComponentsMap: {[key: string]: Type diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss index 124b6c4d99..3143ab1ee2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss @@ -26,6 +26,13 @@ outline: none; transition: all .2s ease-in-out; + + &.tb-overflow-visible { + overflow: visible; + .tb-widget { + overflow: visible; + } + } } div.tb-widget { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts index f4b54a395d..81c206391a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts @@ -22,7 +22,6 @@ import { ElementRef, EventEmitter, HostBinding, - Inject, Input, OnDestroy, OnInit, @@ -36,10 +35,9 @@ import { DashboardWidget, DashboardWidgets } from '@home/models/dashboard-compon import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { SafeStyle } from '@angular/platform-browser'; -import { guid, isNotEmptyStr } from '@core/utils'; -import cssjs from '@core/css/css'; -import { DOCUMENT } from '@angular/common'; +import { isNotEmptyStr } from '@core/utils'; import { GridsterItemComponent } from 'angular-gridster2'; +import { UtilsService } from '@core/services/utils.service'; export enum WidgetComponentActionType { MOUSE_DOWN, @@ -86,6 +84,9 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, A @Input() isEdit: boolean; + @Input() + isPreview: boolean; + @Input() isMobile: boolean; @@ -115,7 +116,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, A constructor(protected store: Store, private cd: ChangeDetectorRef, private renderer: Renderer2, - @Inject(DOCUMENT) private document: Document) { + private utils: UtilsService) { super(store); } @@ -123,12 +124,8 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, A this.widget.widgetContext.containerChangeDetector = this.cd; const cssString = this.widget.widget.config.widgetCss; if (isNotEmptyStr(cssString)) { - const cssParser = new cssjs(); - cssParser.testMode = false; - this.cssClass = 'tb-widget-css-' + guid(); - this.renderer.addClass(this.gridsterItem.el, this.cssClass); - cssParser.cssPreviewNamespace = this.cssClass; - cssParser.createStyleElement(this.cssClass, cssString); + this.cssClass = + this.utils.applyCssToElement(this.renderer, this.gridsterItem.el, 'tb-widget-css', cssString); } } @@ -138,10 +135,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, A ngOnDestroy(): void { if (this.cssClass) { - const el = this.document.getElementById(this.cssClass); - if (el) { - el.parentNode.removeChild(el); - } + this.utils.clearCssElement(this.renderer, this.cssClass); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html index 38c4527074..3a9ced83de 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html @@ -25,6 +25,7 @@ [autofillHeight]="true" [columns]="24" [isEdit]="false" + [isPreview]="true" [isMobileDisabled]="true" [isEditActionEnabled]="false" [isRemoveActionEnabled]="false"> diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index a7fbc64db7..a79fa89242 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -131,6 +131,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI @Input() isEdit: boolean; + @Input() + isPreview: boolean; + @Input() isMobile: boolean; @@ -231,6 +234,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.widgetContext.store = this.store; this.widgetContext.servicesMap = ServicesMap; this.widgetContext.isEdit = this.isEdit; + this.widgetContext.isPreview = this.isPreview; this.widgetContext.isMobile = this.isMobile; this.widgetContext.toastTargetId = this.toastTargetId; @@ -252,6 +256,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI handleWidgetAction: this.handleWidgetAction.bind(this), elementClick: this.elementClick.bind(this), cardClick: this.cardClick.bind(this), + click: this.click.bind(this), getActiveEntityInfo: this.getActiveEntityInfo.bind(this), openDashboardStateInSeparateDialog: this.openDashboardStateInSeparateDialog.bind(this), openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this) @@ -411,6 +416,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.widgetType = this.widgetInfo.widgetTypeFunction; this.typeParameters = this.widgetInfo.typeParameters; this.widgetContext.embedTitlePanel = this.typeParameters.embedTitlePanel; + this.widgetContext.overflowVisible = this.typeParameters.overflowVisible; if (!this.widgetType) { this.widgetTypeInstance = {}; @@ -1083,6 +1089,9 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.router.navigateByUrl(url); } break; + case WidgetActionType.openURL: + window.open(descriptor.url, descriptor.openNewBrowserTab ? '_blank' : '_self'); + break; case WidgetActionType.custom: const customFunction = descriptor.customFunction; if (customFunction && customFunction.length > 0) { @@ -1345,7 +1354,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.widgetContext.parentDashboard : this.widgetContext.dashboard, popoverComponent: componentRef.instance }, - {width: popoverWidth, height: popoverHeight}, + {width: popoverWidth || '25vw', height: popoverHeight || '25vh'}, popoverStyle, {} ); @@ -1423,7 +1432,15 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI } private cardClick($event: Event) { - const descriptors = this.getActionDescriptors('cardClick'); + this.onClick($event, 'cardClick'); + } + + private click($event: Event) { + this.onClick($event, 'click'); + } + + private onClick($event: Event, sourceId: string) { + const descriptors = this.getActionDescriptors(sourceId); if (descriptors.length) { $event.stopPropagation(); const descriptor = descriptors[0]; diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index 2217b5bf5d..d6c07004c0 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -15,7 +15,7 @@ /// import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { startWith, skip, Subject } from 'rxjs'; +import { skip, startWith, Subject } from 'rxjs'; import { Store } from '@ngrx/store'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'; @@ -32,6 +32,8 @@ import { instanceOfSearchableComponent, ISearchableComponent } from '@home/model import { ActiveComponentService } from '@core/services/active-component.service'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { isDefined, isDefinedAndNotNull } from '@core/utils'; @Component({ selector: 'tb-home', @@ -70,8 +72,8 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni constructor(protected store: Store, @Inject(WINDOW) private window: Window, private activeComponentService: ActiveComponentService, - public breakpointObserver: BreakpointObserver, - private fb: FormBuilder) { + private fb: FormBuilder, + public breakpointObserver: BreakpointObserver) { super(store); } @@ -144,9 +146,18 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni private updateActiveComponent(activeComponent: any) { this.showSearch = false; + this.hideLoadingBar = false; this.textSearch.reset('', {emitEvent: false}); this.activeComponent = activeComponent; - this.hideLoadingBar = activeComponent && activeComponent instanceof RouterTabsComponent; + + if (activeComponent && activeComponent instanceof RouterTabsComponent + && isDefinedAndNotNull(this.activeComponent.activatedRoute?.snapshot?.data?.showMainLoadingBar)) { + this.hideLoadingBar = !this.activeComponent.activatedRoute.snapshot.data.showMainLoadingBar; + } else if (activeComponent && activeComponent instanceof PageComponent + && isDefinedAndNotNull(this.activeComponent?.showMainLoadingBar)) { + this.hideLoadingBar = !this.activeComponent.showMainLoadingBar; + } + if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { this.searchEnabled = true; this.searchableComponent = this.activeComponent; diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 4a7fc32502..2ea34377d6 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -57,6 +57,7 @@ import { createLabelFromDatasource, createLabelFromSubscriptionEntityInfo, formatValue, + getEntityDetailsPageURL, hasDatasourceLabelsVariables, isDefined } from '@core/utils'; @@ -253,7 +254,8 @@ export class WidgetContext { }; utils: IWidgetUtils = { - formatValue + formatValue, + getEntityDetailsPageURL }; $widgetElement: JQuery; @@ -263,6 +265,7 @@ export class WidgetContext { height: number; $scope: IDynamicWidgetComponent; isEdit: boolean; + isPreview: boolean; isMobile: boolean; toastTargetId: string; @@ -279,6 +282,7 @@ export class WidgetContext { timeWindow?: WidgetTimewindow; embedTitlePanel?: boolean; + overflowVisible?: boolean; hideTitlePanel = false; @@ -345,35 +349,35 @@ export class WidgetContext { showSuccessToast(message: string, duration: number = 1000, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', - target: string = 'dashboardRoot') { - this.showToast('success', message, duration, verticalPosition, horizontalPosition, target); + target: string = 'dashboardRoot', modern = false) { + this.showToast('success', message, duration, verticalPosition, horizontalPosition, target, modern); } showInfoToast(message: string, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', - target: string = 'dashboardRoot') { - this.showToast('info', message, undefined, verticalPosition, horizontalPosition, target); + target: string = 'dashboardRoot', modern = false) { + this.showToast('info', message, undefined, verticalPosition, horizontalPosition, target, modern); } showWarnToast(message: string, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', - target: string = 'dashboardRoot') { - this.showToast('warn', message, undefined, verticalPosition, horizontalPosition, target); + target: string = 'dashboardRoot', modern = false) { + this.showToast('warn', message, undefined, verticalPosition, horizontalPosition, target, modern); } showErrorToast(message: string, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', - target: string = 'dashboardRoot') { - this.showToast('error', message, undefined, verticalPosition, horizontalPosition, target); + target: string = 'dashboardRoot', modern = false) { + this.showToast('error', message, undefined, verticalPosition, horizontalPosition, target, modern); } showToast(type: NotificationType, message: string, duration: number, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', - target: string = 'dashboardRoot') { + target: string = 'dashboardRoot', modern = false) { this.store.dispatch(new ActionNotificationShow( { message, @@ -383,7 +387,8 @@ export class WidgetContext { horizontalPosition, target, panelClass: this.widgetNamespace, - forceDismiss: true + forceDismiss: true, + modern })); } diff --git a/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts b/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts index 252250ece2..20e6cc73e1 100644 --- a/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts @@ -33,6 +33,7 @@ const routes: Routes = [ component: RouterTabsComponent, data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + showMainLoadingBar: false, breadcrumb: { label: 'account.account', icon: 'account_circle' diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 74c5012791..63fb9a14de 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -136,6 +136,7 @@ const routes: Routes = [ component: RouterTabsComponent, data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + showMainLoadingBar: false, breadcrumb: { label: 'admin.settings', icon: 'settings' diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 4b0ba741d3..1d346c70f1 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -15,7 +15,14 @@ /// import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, ValidationErrors, Validators } from '@angular/forms'; +import { + AbstractControl, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, + ValidationErrors, + Validators +} from '@angular/forms'; import { ClientAuthenticationMethod, DomainSchema, @@ -26,9 +33,11 @@ import { MapperConfigType, OAuth2ClientRegistrationTemplate, OAuth2DomainInfo, - OAuth2Info, OAuth2MobileInfo, + OAuth2Info, + OAuth2MobileInfo, OAuth2ParamsInfo, - OAuth2RegistrationInfo, PlatformType, + OAuth2RegistrationInfo, + PlatformType, platformTypeTranslations, TenantNameStrategy } from '@shared/models/oauth2.models'; @@ -105,6 +114,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha templateProvider = ['Custom']; + showMainLoadingBar = false; + private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl; private static validateScope(control: AbstractControl): ValidationErrors | null { diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index e4fd483eb9..17dda4a0c9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -23,7 +23,8 @@ import { AbstractControl, UntypedFormBuilder, UntypedFormControl, - UntypedFormGroup, ValidationErrors, + UntypedFormGroup, + ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; @@ -35,10 +36,7 @@ import { randomAlphanumeric } from '@core/utils'; import { AuthService } from '@core/auth/auth.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { forkJoin, Observable, of } from 'rxjs'; -import { MatCheckboxChange } from '@angular/material/checkbox'; -import { AlarmInfo } from '@shared/models/alarm.models'; -import { QueueProcessingStrategyTypes, QueueProcessingStrategyTypesMap } from '@shared/models/queue.models'; +import { Observable, of } from 'rxjs'; @Component({ selector: 'tb-security-settings', @@ -50,6 +48,8 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi securitySettingsFormGroup: UntypedFormGroup; jwtSecuritySettingsFormGroup: UntypedFormGroup; + showMainLoadingBar = false; + private securitySettings: SecuritySettings; private jwtSettings: JwtSettings; diff --git a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts index 5ea4776ff9..5355114586 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts @@ -47,6 +47,8 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI twoFactorAuthProviderType = TwoFactorAuthProviderType; twoFactorAuthProvidersData = twoFactorAuthProvidersData; + showMainLoadingBar = false; + @ViewChildren(MatExpansionPanel) expansionPanel: QueryList; constructor(protected store: Store, diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.models.ts b/ui-ngx/src/app/modules/home/pages/home-pages.models.ts index 20872e3c65..848bb0b85c 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.models.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.models.ts @@ -30,4 +30,3 @@ export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction + + {{ 'notification.edge-trigger-settings' | translate }} + +
+
+ notification.filter + + + + notification.notify-on + + + {{ edgeConnectionEventTranslationMap.get(edgeEvent) | translate }} + + + +
+
+ +
+
+ + notification.description + + +
+
+
+ + + {{ 'notification.edge-trigger-settings' | translate }} +
+
+
+ notification.filter + + +
+
+
+
+
+ + notification.description + + +
+
+
+ {{ 'notification.entities-limit-trigger-settings' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index c3cf4581e2..89488b7517 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -68,6 +68,7 @@ import { } from '@shared/models/api-usage.models'; import { LimitedApi, LimitedApiTranslationMap } from '@shared/models/limited-api.models'; import { StringItemsOption } from '@shared/components/string-items-list.component'; +import { EdgeConnectionEvent, EdgeConnectionEventTranslationMap } from '@shared/models/edge.models'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -98,6 +99,8 @@ export class RuleNotificationDialogComponent extends apiUsageLimitTemplateForm: FormGroup; newPlatformVersionTemplateForm: FormGroup; rateLimitsTemplateForm: FormGroup; + edgeCommunicationFailureTemplateForm: FormGroup; + edgeConnectionTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -132,6 +135,9 @@ export class RuleNotificationDialogComponent extends apiFeatures: ApiFeature[] = Object.values(ApiFeature); apiFeatureTranslationMap = ApiFeatureTranslationMap; + edgeConnectionEvents: EdgeConnectionEvent[] = Object.values(EdgeConnectionEvent); + edgeConnectionEventTranslationMap = EdgeConnectionEventTranslationMap; + limitedApis: StringItemsOption[]; entityType = EntityType; @@ -221,6 +227,19 @@ export class RuleNotificationDialogComponent extends } }); + this.edgeConnectionTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + edges: [null], + notifyOn: [null] + }) + }); + + this.edgeCommunicationFailureTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + edges: [null] + }) + }); + this.alarmTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ alarmTypes: [null], @@ -328,7 +347,9 @@ export class RuleNotificationDialogComponent extends [TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm], [TriggerType.API_USAGE_LIMIT, this.apiUsageLimitTemplateForm], [TriggerType.NEW_PLATFORM_VERSION, this.newPlatformVersionTemplateForm], - [TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm] + [TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm], + [TriggerType.EDGE_COMMUNICATION_FAILURE, this.edgeCommunicationFailureTemplateForm], + [TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm] ]); if (data.isAdd || data.isCopy) { diff --git a/ui-ngx/src/app/shared/components/button/widget-button.component.html b/ui-ngx/src/app/shared/components/button/widget-button.component.html new file mode 100644 index 0000000000..03b5dfb75a --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.html @@ -0,0 +1,39 @@ + + diff --git a/ui-ngx/src/app/shared/components/button/widget-button.component.scss b/ui-ngx/src/app/shared/components/button/widget-button.component.scss new file mode 100644 index 0000000000..a0abc95731 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.scss @@ -0,0 +1,189 @@ +/** + * Copyright © 2016-2024 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. + */ +$defaultMainColor: #3F52DD; +$defaultBackgroundColor: #FFFFFF; +$defaultBoxShadowColor: rgba(0, 0, 0, 0.08); +$defaultDisabledBoxShadowColor: rgba(0, 0, 0, 0); + +$defaultMainColorDisabled: rgba(0, 0, 0, 0.38); +$defaultBackgroundColorDisabled: rgba(0, 0, 0, 0.03); + +$mainColorEnabled: var(--tb-widget-button-main-color-enabled, $defaultMainColor); +$backgroundColorEnabled: var(--tb-widget-button-background-color-enabled, $defaultBackgroundColor); +$boxShadowColorEnabled: var(--tb-widget-button-box-shadow-color-enabled, $defaultBoxShadowColor); + +$mainColorHovered: var(--tb-widget-button-main-color-hovered, $defaultMainColor); +$backgroundColorHovered: var(--tb-widget-button-background-color-hovered, $defaultBackgroundColor); +$boxShadowColorHovered: var(--tb-widget-button-box-shadow-color-hovered, $defaultBoxShadowColor); +$mainColorHoveredFilled: var(--tb-widget-button-main-color-hovered-filled, #3347db); // main.darken(6) + +$mainColorPressed: var(--tb-widget-button-main-color-pressed, $defaultMainColor); +$backgroundColorPressed: var(--tb-widget-button-background-color-pressed, $defaultBackgroundColor); +$boxShadowColorPressed: var(--tb-widget-button-box-shadow-color-pressed, $defaultBoxShadowColor); +$mainColorPressedFilled: var(--tb-widget-button-main-color-pressed-filled, #273cd9); // main.darken(12) +$mainColorPressedRipple: var(--tb-widget-button-main-color-pressed-ripple, rgba(63, 82, 221, 0.1)); // Alpha(Main, 0.1) +$mainColorPressedRippleFilled: var(--tb-widget-button-main-color-pressed-ripple-filled, #2439cd); // main.darken(18) + +$mainColorActivated: var(--tb-widget-button-main-color-activated, $defaultMainColor); +$backgroundColorActivated: var(--tb-widget-button-background-color-activated, $defaultBackgroundColor); +$boxShadowColorActivated: var(--tb-widget-button-box-shadow-color-activated, $defaultBoxShadowColor); +$mainColorActivatedFilled: var(--tb-widget-button-main-color-activated-filled, #273cd9); // main.darken(12) + +$mainColorDisabled: var(--tb-widget-button-main-color-disabled, $defaultMainColorDisabled); +$backgroundColorDisabled: var(--tb-widget-button-background-color-disabled, $defaultBackgroundColorDisabled); +$boxShadowColorDisabled: var(--tb-widget-button-box-shadow-color-disabled, $defaultBoxShadowColor); + + +@mixin _tb-widget-button-styles($main, $background, $boxShadow) { + color: $main; + background-color: $background; + box-shadow: 0 4px 8px 0 $boxShadow; + &.tb-outlined { + border: 1px solid $main; + } + &.tb-filled { + color: $background; + background-color: $main; + } + &.tb-underlined { + border-bottom: 2px solid $main; + } + &.tb-basic { + background-color: transparent; + } +} + + +.mat-mdc-button.mat-mdc-button-base.tb-widget-button { + width: 100%; + height: 100%; + padding: 8px 12px; + .mdc-button__label { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + .tb-widget-button-content { + width: 100%; + display: flex; + flex-direction: row; + gap: 4px; + justify-content: center; + align-items: center; + .mat-icon { + margin: 0; + } + span.tb-widget-button-label { + line-height: normal; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .mat-mdc-button-persistent-ripple::before { + opacity: 0; + } + + @include _tb-widget-button-styles($mainColorEnabled, $backgroundColorEnabled, $boxShadowColorEnabled); + + &:not(:disabled):not(.tb-disabled-state) { + &:hover, &.tb-hover-state { + &:not(:active):not(.tb-active-state) { + &:not(.tb-filled) { + .mat-mdc-button-persistent-ripple::before { + opacity: 0.04; + background-color: $mainColorHovered; + } + } + &.tb-filled { + .mat-mdc-button-persistent-ripple::before { + opacity: 1; + background-color: $mainColorHoveredFilled; + } + } + @include _tb-widget-button-styles($mainColorHovered, $backgroundColorHovered, $boxShadowColorHovered); + } + } + &.tb-pressed-state { + &:not(.tb-filled) { + .mat-mdc-button-ripple { + background-color: $mainColorPressedRipple; + } + } + &.tb-filled { + .mat-mdc-button-ripple { + background-color: $mainColorPressedRippleFilled; + } + } + } + &.tb-pressed { + &:not(.tb-filled) { + .mat-ripple-element { + background-color: $mainColorPressedRipple; + } + } + &.tb-filled { + .mat-ripple-element { + background-color: $mainColorPressedRippleFilled; + } + } + } + &.tb-pressed, &.tb-pressed-state { + &:not(.tb-filled) { + .mat-mdc-button-persistent-ripple::before { + opacity: 0.12; + background-color: $mainColorPressed; + } + } + &.tb-filled { + .mat-mdc-button-persistent-ripple::before { + opacity: 1; + background-color: $mainColorPressedFilled; + } + } + @include _tb-widget-button-styles($mainColorPressed, $backgroundColorPressed, $boxShadowColorPressed); + } + &:active, &.tb-active-state { + &:not(.tb-pressed):not(.tb-pressed-state) { + &:not(.tb-filled) { + .mat-mdc-button-persistent-ripple::before { + opacity: 0.12; + background-color: $mainColorActivated; + } + } + &.tb-filled { + .mat-mdc-button-persistent-ripple::before { + opacity: 1; + background-color: $mainColorActivatedFilled; + } + } + @include _tb-widget-button-styles($mainColorActivated, $backgroundColorActivated, $boxShadowColorActivated); + } + } + } + + &:disabled, &.tb-disabled-state { + &:not(.tb-filled) { + @include _tb-widget-button-styles($mainColorDisabled, $backgroundColorDisabled, $boxShadowColorDisabled); + } + &.tb-filled { + @include _tb-widget-button-styles($backgroundColorDisabled, $mainColorDisabled, $boxShadowColorDisabled); + } + } +} diff --git a/ui-ngx/src/app/shared/components/button/widget-button.component.ts b/ui-ngx/src/app/shared/components/button/widget-button.component.ts new file mode 100644 index 0000000000..059f263196 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.ts @@ -0,0 +1,195 @@ +/// +/// Copyright © 2016-2024 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 { + AfterViewInit, + Component, + ElementRef, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + Renderer2, + SimpleChanges, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + generateWidgetButtonAppearanceCss, + widgetButtonDefaultAppearance +} from '@shared/components/button/widget-button.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ComponentStyle, iconStyle } from '@shared/models/widget-settings.models'; +import { UtilsService } from '@core/services/utils.service'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { Observable, of } from 'rxjs'; +import { WidgetContext } from '@home/models/widget-component.models'; + +const initialButtonHeight = 60; +const horizontalLayoutPadding = 24; +const verticalLayoutPadding = 16; + +@Component({ + selector: 'tb-widget-button', + templateUrl: './widget-button.component.html', + styleUrls: ['./widget-button.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges { + + @ViewChild('widgetButton', {read: ElementRef}) + widgetButton: ElementRef; + + @ViewChild('widgetButtonContent', {static: false}) + widgetButtonContent: ElementRef; + + @Input() + appearance = widgetButtonDefaultAppearance; + + @Input() + borderRadius = '4px'; + + @Input() + @coerceBoolean() + disabled = false; + + @Input() + @coerceBoolean() + activated = false; + + @Input() + @coerceBoolean() + hovered = false; + + @Input() + @coerceBoolean() + pressed = false; + + @Input() + @coerceBoolean() + disableEvents = false; + + @Input() + ctx: WidgetContext; + + @Output() + clicked = new EventEmitter(); + + label$: Observable; + + iconStyle: ComponentStyle = {}; + + mousePressed = false; + + private buttonResize$: ResizeObserver; + + private appearanceCssClass: string; + + constructor(private renderer: Renderer2, + private elementRef: ElementRef, + private utils: UtilsService) {} + + ngOnInit(): void { + this.updateAppearance(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange) { + if (propName === 'appearance') { + this.updateAppearance(); + } + } + } + } + + ngAfterViewInit(): void { + this.updateAutoScale(); + } + + ngOnDestroy(): void { + if (this.buttonResize$) { + this.buttonResize$.disconnect(); + } + this.clearAppearanceCss(); + } + + public validateSize() { + if (this.appearance.autoScale && this.widgetButton.nativeElement) { + this.onResize(); + } + } + + private updateAppearance(): void { + this.clearAppearanceCss(); + if (this.appearance.showIcon) { + this.iconStyle = iconStyle(this.appearance.iconSize, this.appearance.iconSizeUnit); + } + if (this.appearance.showLabel) { + this.label$ = this.ctx ? this.ctx.registerLabelPattern(this.appearance.label, this.label$) : of(this.appearance.label); + } + const appearanceCss = generateWidgetButtonAppearanceCss(this.appearance); + this.appearanceCssClass = this.utils.applyCssToElement(this.renderer, this.elementRef.nativeElement, + 'tb-widget-button', appearanceCss); + this.updateAutoScale(); + } + + private clearAppearanceCss(): void { + if (this.appearanceCssClass) { + this.utils.clearCssElement(this.renderer, this.appearanceCssClass, this.elementRef?.nativeElement); + this.appearanceCssClass = null; + } + } + + private updateAutoScale() { + if (this.buttonResize$) { + this.buttonResize$.disconnect(); + } + if (this.widgetButton && this.widgetButtonContent) { + if (this.appearance.autoScale) { + this.buttonResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.buttonResize$.observe(this.widgetButton.nativeElement); + this.onResize(); + } else { + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'transform', 'none'); + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'width', '100%'); + } + } + } + + private onResize() { + const height = this.widgetButton.nativeElement.getBoundingClientRect().height; + const buttonScale = height / initialButtonHeight; + const paddingScale = Math.min(buttonScale, 1); + const buttonWidth = this.widgetButton.nativeElement.getBoundingClientRect().width - (horizontalLayoutPadding * paddingScale); + const buttonHeight = this.widgetButton.nativeElement.getBoundingClientRect().height - (verticalLayoutPadding * paddingScale); + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'transform', `scale(1)`); + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'width', 'auto'); + const contentWidth = this.widgetButtonContent.nativeElement.getBoundingClientRect().width; + const contentHeight = this.widgetButtonContent.nativeElement.getBoundingClientRect().height; + const maxScale = Math.max(1, buttonScale); + const scale = Math.min(Math.min(buttonWidth / contentWidth, buttonHeight / contentHeight), maxScale); + const targetWidth = buttonWidth / scale; + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'width', targetWidth + 'px'); + this.renderer.setStyle(this.widgetButtonContent.nativeElement, 'transform', `scale(${scale})`); + } + +} diff --git a/ui-ngx/src/app/shared/components/button/widget-button.models.ts b/ui-ngx/src/app/shared/components/button/widget-button.models.ts new file mode 100644 index 0000000000..34f13864a9 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.models.ts @@ -0,0 +1,271 @@ +/// +/// Copyright © 2016-2024 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 { cssUnit } from '@shared/models/widget-settings.models'; +import tinycolor from 'tinycolor2'; + +const defaultMainColor = '#3F52DD'; +const defaultBackgroundColor = '#FFFFFF'; + +const hoveredFilledDarkenAmount = 6; +const pressedFilledDarkenAmount = 12; +const activatedFilledDarkenAmount = 12; +const pressedRippleFilledDarkenAmount = 18; + +export const defaultMainColorDisabled = 'rgba(0, 0, 0, 0.38)'; +export const defaultBackgroundColorDisabled = 'rgba(0, 0, 0, 0.03)'; + +const defaultBoxShadowColor = 'rgba(0, 0, 0, 0.08)'; +const defaultDisabledBoxShadowColor = 'rgba(0, 0, 0, 0)'; + +export enum WidgetButtonType { + outlined = 'outlined', + filled = 'filled', + underlined = 'underlined', + basic = 'basic' +} + +export const widgetButtonTypes = Object.keys(WidgetButtonType) as WidgetButtonType[]; + +export const widgetButtonTypeTranslations = new Map( + [ + [WidgetButtonType.outlined, 'widgets.button.outlined'], + [WidgetButtonType.filled, 'widgets.button.filled'], + [WidgetButtonType.underlined, 'widgets.button.underlined'], + [WidgetButtonType.basic, 'widgets.button.basic'] + ] +); + +export const widgetButtonTypeImages = new Map( + [ + [WidgetButtonType.outlined, 'assets/widget/button/outlined.svg'], + [WidgetButtonType.filled, 'assets/widget/button/filled.svg'], + [WidgetButtonType.underlined, 'assets/widget/button/underlined.svg'], + [WidgetButtonType.basic, 'assets/widget/button/basic.svg'] + ] +); + +export enum WidgetButtonState { + enabled = 'enabled', + hovered = 'hovered', + pressed = 'pressed', + activated = 'activated', + disabled = 'disabled' +} + +export const widgetButtonStates = Object.keys(WidgetButtonState) as WidgetButtonState[]; + +export const widgetButtonStatesTranslations = new Map( + [ + [WidgetButtonState.enabled, 'widgets.button-state.enabled'], + [WidgetButtonState.hovered, 'widgets.button-state.hovered'], + [WidgetButtonState.pressed, 'widgets.button-state.pressed'], + [WidgetButtonState.activated, 'widgets.button-state.activated'], + [WidgetButtonState.disabled, 'widgets.button-state.disabled'] + ] +); + +export interface WidgetButtonCustomStyle { + overrideMainColor?: boolean; + mainColor?: string; + overrideBackgroundColor?: boolean; + backgroundColor?: string; + overrideDropShadow?: boolean; + dropShadow?: boolean; +} + +export type WidgetButtonCustomStyles = Record; + +export interface WidgetButtonAppearance { + type: WidgetButtonType; + autoScale: boolean; + showLabel: boolean; + label: string; + showIcon: boolean; + icon: string; + iconSize: number; + iconSizeUnit: cssUnit; + mainColor: string; + backgroundColor: string; + customStyle: WidgetButtonCustomStyles; +} + +export const widgetButtonDefaultAppearance: WidgetButtonAppearance = { + type: WidgetButtonType.outlined, + autoScale: true, + showLabel: true, + label: 'Button', + showIcon: true, + icon: 'home', + iconSize: 24, + iconSizeUnit: 'px', + mainColor: defaultMainColor, + backgroundColor: defaultBackgroundColor, + customStyle: { + enabled: null, + hovered: null, + pressed: null, + activated: null, + disabled: null + } +}; + +const mainColorVarPrefix = '--tb-widget-button-main-color-'; +const backgroundColorVarPrefix = '--tb-widget-button-background-color-'; +const boxShadowColorVarPrefix = '--tb-widget-button-box-shadow-color-'; + +abstract class ButtonStateCssGenerator { + + constructor() {} + + public generateStateCss(appearance: WidgetButtonAppearance): string { + let mainColor = this.getMainColor(appearance); + let backgroundColor = this.getBackgroundColor(appearance); + const shadowEnabledByDefault = appearance.type !== WidgetButtonType.basic; + let shadowColor = shadowEnabledByDefault ? defaultBoxShadowColor : defaultDisabledBoxShadowColor; + const stateCustomStyle = appearance.customStyle[this.state]; + if (stateCustomStyle?.overrideMainColor && stateCustomStyle?.mainColor) { + mainColor = stateCustomStyle.mainColor; + } + if (stateCustomStyle?.overrideBackgroundColor && stateCustomStyle?.backgroundColor) { + backgroundColor = stateCustomStyle.backgroundColor; + } + if (stateCustomStyle?.overrideDropShadow) { + shadowColor = !!stateCustomStyle.dropShadow ? defaultBoxShadowColor : defaultDisabledBoxShadowColor; + } + + let css = `${mainColorVarPrefix}${this.state}: ${mainColor};\n`+ + `${backgroundColorVarPrefix}${this.state}: ${backgroundColor};\n`+ + `${boxShadowColorVarPrefix}${this.state}: ${shadowColor};`; + const additionalCss = this.generateAdditionalStateCss(mainColor, backgroundColor); + if (additionalCss) { + css += `\n${additionalCss}`; + } + return css; + } + + protected abstract get state(): WidgetButtonState; + + protected getMainColor(appearance: WidgetButtonAppearance): string { + return appearance.mainColor || defaultMainColor; + } + + protected getBackgroundColor(appearance: WidgetButtonAppearance): string { + return appearance.backgroundColor || defaultBackgroundColor; + } + + protected generateAdditionalStateCss(_mainColor: string, _backgroundColor: string): string { + return null; + } +} + +class EnabledButtonStateCssGenerator extends ButtonStateCssGenerator { + + protected get state(): WidgetButtonState { + return WidgetButtonState.enabled; + } +} + +class HoveredButtonStateCssGenerator extends ButtonStateCssGenerator { + + protected get state(): WidgetButtonState { + return WidgetButtonState.hovered; + } + + protected generateAdditionalStateCss(mainColor: string): string { + const mainColorHoveredFilled = darkenColor(mainColor, hoveredFilledDarkenAmount); + return `--tb-widget-button-main-color-hovered-filled: ${mainColorHoveredFilled};`; + } +} + +class PressedButtonStateCssGenerator extends ButtonStateCssGenerator { + + protected get state(): WidgetButtonState { + return WidgetButtonState.pressed; + } + + protected generateAdditionalStateCss(mainColor: string): string { + const mainColorPressedFilled = darkenColor(mainColor, pressedFilledDarkenAmount); + const mainColorInstance = tinycolor(mainColor); + const mainColorPressedRipple = mainColorInstance.setAlpha(mainColorInstance.getAlpha() * 0.1).toRgbString(); + const mainColorPressedRippleFilled = darkenColor(mainColor, pressedRippleFilledDarkenAmount); + return `--tb-widget-button-main-color-pressed-filled: ${mainColorPressedFilled};\n`+ + `--tb-widget-button-main-color-pressed-ripple: ${mainColorPressedRipple};\n`+ + `--tb-widget-button-main-color-pressed-ripple-filled: ${mainColorPressedRippleFilled};`; + } +} + +class ActivatedButtonStateCssGenerator extends ButtonStateCssGenerator { + + protected get state(): WidgetButtonState { + return WidgetButtonState.activated; + } + + protected generateAdditionalStateCss(mainColor: string): string { + const mainColorActivatedFilled = darkenColor(mainColor, activatedFilledDarkenAmount); + return `--tb-widget-button-main-color-activated-filled: ${mainColorActivatedFilled};`; + } +} + +class DisabledButtonStateCssGenerator extends ButtonStateCssGenerator { + + protected get state(): WidgetButtonState { + return WidgetButtonState.disabled; + } + + protected getMainColor(): string { + return defaultMainColorDisabled; + } + + protected getBackgroundColor(): string { + return defaultBackgroundColorDisabled; + } +} + +const buttonStateCssGeneratorsMap = new Map( + [ + [WidgetButtonState.enabled, new EnabledButtonStateCssGenerator()], + [WidgetButtonState.hovered, new HoveredButtonStateCssGenerator()], + [WidgetButtonState.pressed, new PressedButtonStateCssGenerator()], + [WidgetButtonState.activated, new ActivatedButtonStateCssGenerator()], + [WidgetButtonState.disabled, new DisabledButtonStateCssGenerator()] + ] +); + +const widgetButtonCssSelector = '.mat-mdc-button.mat-mdc-button-base.tb-widget-button'; + +export const generateWidgetButtonAppearanceCss = (appearance: WidgetButtonAppearance): string => { + let statesCss = ''; + for (const state of widgetButtonStates) { + const generator = buttonStateCssGeneratorsMap.get(state); + statesCss += `\n${generator.generateStateCss(appearance)}`; + } + return `${widgetButtonCssSelector} {\n`+ + `${statesCss}\n`+ + `}`; +}; + +const darkenColor = (inputColor: string, amount: number): string => { + const input = tinycolor(inputColor); + const brightness = input.getBrightness() / 255; + let ratio: number; + if (brightness >= 0.4 && brightness <= 0.5) { + ratio = brightness + 0.2; + } else { + ratio = Math.max(0.1, Math.log10(brightness * 8)); + } + return input.darken(ratio * amount).toRgbString(); +}; diff --git a/ui-ngx/src/app/shared/components/color-input.component.ts b/ui-ngx/src/app/shared/components/color-input.component.ts index 171e8bf7d8..ad772ba861 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.ts +++ b/ui-ngx/src/app/shared/components/color-input.component.ts @@ -189,7 +189,7 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro this.popoverService.hidePopover(trigger); } else { const colorPickerPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, ColorPickerPanelComponent, 'left', true, null, + this.viewContainerRef, ColorPickerPanelComponent, ['leftTopOnly', 'leftOnly', 'leftBottomOnly'], true, null, { color: this.colorFormGroup.get('color').value, colorClearButton: this.colorClearButton diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss index c55222a7d2..9aca580994 100644 --- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss @@ -18,9 +18,11 @@ display: flex; flex-direction: column; gap: 32px; + overflow: auto; .saturation-component { height: 238px; + min-height: 80px; border-radius: 8px; } diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html index e9f177e851..ab3694773e 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html @@ -15,9 +15,14 @@ limitations under the License. --> - - {{ label }} + + {{ label }} close + + warning + - + - + diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts index 308087bd31..2cdaf2d13c 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.ts @@ -15,7 +15,13 @@ /// import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { Observable, of } from 'rxjs'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; @@ -28,11 +34,11 @@ import { AppState } from '@app/core/core.state'; import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { TranslateService } from '@ngx-translate/core'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; import { getEntityDetailsPageURL } from '@core/utils'; import { EntityType } from '@shared/models/entity-type.models'; import { AuthUser } from '@shared/models/user.model'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-dashboard-autocomplete', @@ -82,14 +88,16 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI @Input() subscriptSizing: SubscriptSizing = 'fixed'; - private requiredValue: boolean; - get required(): boolean { - return this.requiredValue; - } @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } + @coerceBoolean() + inlineField: boolean; + + @Input() + requiredText: string; + + @Input() + @coerceBoolean() + required: boolean; @Input() disabled: boolean; @@ -106,7 +114,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI private authUser: AuthUser; - private propagateChange = (v: any) => { }; + private propagateChange = (_v: any) => { }; constructor(private store: Store, public translate: TranslateService, @@ -118,7 +126,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI } this.selectDashboardFormGroup = this.fb.group({ - dashboard: [null] + dashboard: [null, this.required ? [Validators.required] : []] }); } @@ -126,7 +134,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } ngOnInit() { @@ -134,7 +142,7 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI .pipe( debounceTime(150), tap(value => { - let modelValue; + let modelValue: string | DashboardInfo; if (typeof value === 'string' || !value) { modelValue = null; } else { @@ -218,15 +226,13 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI fetchDashboards(searchText?: string): Observable> { this.searchText = searchText; - const pageLink = new PageLink(10, 0, searchText, { + const pageLink = new PageLink(25, 0, searchText, { property: 'title', direction: Direction.ASC }); return this.getDashboards(pageLink).pipe( catchError(() => of(emptyPageData())), - map(pageData => { - return pageData.data; - }) + map(pageData => pageData.data) ); } diff --git a/ui-ngx/src/app/shared/components/image/image-dialog.component.ts b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts index 434d76d74d..1a777d1bf7 100644 --- a/ui-ngx/src/app/shared/components/image/image-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts @@ -154,7 +154,7 @@ export class ImageDialogComponent extends this.imageChanged = true; this.image = result; this.imagePreviewData = { - url: this.image.public ? this.image.publicLink : this.image.link + url: this.image.public ? `${this.image.publicLink}?ts=${new Date().getTime()}` : this.image.link }; } }); diff --git a/ui-ngx/src/app/shared/components/js-func.component.html b/ui-ngx/src/app/shared/components/js-func.component.html index e072759213..66bced8adf 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.html +++ b/ui-ngx/src/app/shared/components/js-func.component.html @@ -15,14 +15,14 @@ limitations under the License. --> -
- - + + +
+ +
+ +
+
+ +
+
diff --git a/ui-ngx/src/app/shared/components/snack-bar-component.scss b/ui-ngx/src/app/shared/components/snack-bar-component.scss index 97531e220e..d86b740fc3 100644 --- a/ui-ngx/src/app/shared/components/snack-bar-component.scss +++ b/ui-ngx/src/app/shared/components/snack-bar-component.scss @@ -71,4 +71,42 @@ background: #008000; } } + + .tb-modern-toast { + display: flex; + flex-direction: column; + z-index: 1; + &-panel { + display: flex; + padding: 4px 4px 4px 12px; + justify-content: center; + align-items: center; + gap: 4px; + border-radius: 4px; + box-shadow: -2px 2px 4px 0px rgba(0,0,0,0.2); + &.info-toast { + background: #e6e6e6; + color: rgba(50, 50, 50, 1); + } + &.warn-toast { + background: #fff3eb; + color: rgba(220, 109, 27, 1); + + } + &.error-toast { + background-color: #fff2f3; + color: rgba(209, 39, 48, 1); + } + &.success-toast { + background: #ebfcec; + color: rgba(0, 128, 0, 1); + } + } + .toast-text { + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + } + } } diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.scss b/ui-ngx/src/app/shared/components/time/timewindow.component.scss index 92673a41ad..99f3d8367d 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.scss +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.scss @@ -58,6 +58,7 @@ .mdc-button__label { overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } } } diff --git a/ui-ngx/src/app/shared/components/toast.directive.ts b/ui-ngx/src/app/shared/components/toast.directive.ts index 6881460f6a..582be5a577 100644 --- a/ui-ngx/src/app/shared/components/toast.directive.ts +++ b/ui-ngx/src/app/shared/components/toast.directive.ts @@ -84,6 +84,7 @@ export class ToastDirective implements AfterViewInit, OnDestroy { if (hideNotification) { const target = hideNotification.target || 'root'; if (this.toastTarget === target) { + this.currentMessage = null; this.ngZone.run(() => { if (this.snackBarRef) { this.snackBarRef.dismiss(); @@ -280,7 +281,7 @@ export type ToastAnimationState = 'default' | 'opened' | 'closing'; }) export class TbSnackBarComponent implements AfterViewInit, OnDestroy { - @ViewChild('actionButton', {static: true}) actionButton: MatButton; + @ViewChild('actionButton') actionButton: MatButton; @HostBinding('class') get panelClass(): string[] { diff --git a/ui-ngx/src/app/shared/models/action-widget-settings.models.ts b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts new file mode 100644 index 0000000000..a019f086dd --- /dev/null +++ b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts @@ -0,0 +1,132 @@ +/// +/// Copyright © 2016-2024 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 { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { widgetType } from '@shared/models/widget.models'; + +export enum GetValueAction { + DO_NOTHING = 'DO_NOTHING', + EXECUTE_RPC = 'EXECUTE_RPC', + GET_ATTRIBUTE = 'GET_ATTRIBUTE', + GET_TIME_SERIES = 'GET_TIME_SERIES' +} + +export const getValueActions = Object.keys(GetValueAction) as GetValueAction[]; + +export const getValueActionsByWidgetType = (type: widgetType): GetValueAction[] => { + if (type !== widgetType.rpc) { + return getValueActions.filter(action => action !== GetValueAction.EXECUTE_RPC); + } else { + return getValueActions; + } +}; + +export const getValueActionTranslations = new Map( + [ + [GetValueAction.DO_NOTHING, 'widgets.value-action.do-nothing'], + [GetValueAction.EXECUTE_RPC, 'widgets.value-action.execute-rpc'], + [GetValueAction.GET_ATTRIBUTE, 'widgets.value-action.get-attribute'], + [GetValueAction.GET_TIME_SERIES, 'widgets.value-action.get-time-series'] + ] +); + +export interface RpcSettings { + method: string; + requestTimeout: number; + requestPersistent: boolean; + persistentPollingInterval: number; +} + +export interface TelemetryValueSettings { + key: string; +} + +export interface GetAttributeValueSettings extends TelemetryValueSettings { + scope: AttributeScope | null; +} + +export interface SetAttributeValueSettings extends TelemetryValueSettings { + scope: AttributeScope.SERVER_SCOPE | AttributeScope.SHARED_SCOPE; +} + +export enum DataToValueType { + NONE = 'NONE', + FUNCTION = 'FUNCTION' +} + +export interface DataToValueSettings { + type: DataToValueType; + dataToValueFunction: string; + compareToValue?: any; +} + +export interface ValueActionSettings { + actionLabel?: string; +} + +export interface GetValueSettings extends ValueActionSettings { + action: GetValueAction; + defaultValue: V; + executeRpc?: RpcSettings; + getAttribute: GetAttributeValueSettings; + getTimeSeries: TelemetryValueSettings; + dataToValue: DataToValueSettings; +} + +export enum SetValueAction { + EXECUTE_RPC = 'EXECUTE_RPC', + SET_ATTRIBUTE = 'SET_ATTRIBUTE', + ADD_TIME_SERIES = 'ADD_TIME_SERIES' +} + +export const setValueActions = Object.keys(SetValueAction) as SetValueAction[]; + +export const setValueActionsByWidgetType = (type: widgetType): SetValueAction[] => { + if (type !== widgetType.rpc) { + return setValueActions.filter(action => action !== SetValueAction.EXECUTE_RPC); + } else { + return setValueActions; + } +}; + +export const setValueActionTranslations = new Map( + [ + [SetValueAction.EXECUTE_RPC, 'widgets.value-action.execute-rpc'], + [SetValueAction.SET_ATTRIBUTE, 'widgets.value-action.set-attribute'], + [SetValueAction.ADD_TIME_SERIES, 'widgets.value-action.add-time-series'] + ] +); + +export enum ValueToDataType { + VALUE = 'VALUE', + CONSTANT = 'CONSTANT', + FUNCTION = 'FUNCTION', + NONE = 'NONE' +} + +export interface ValueToDataSettings { + type: ValueToDataType; + constantValue: any; + valueToDataFunction: string; +} + +export interface SetValueSettings extends ValueActionSettings { + action: SetValueAction; + executeRpc: RpcSettings; + setAttribute: SetAttributeValueSettings; + putTimeSeries: TelemetryValueSettings; + valueToData: ValueToDataSettings; +} diff --git a/ui-ngx/src/app/shared/models/edge.models.ts b/ui-ngx/src/app/shared/models/edge.models.ts index 7187dc8eaa..569b2bec8a 100644 --- a/ui-ngx/src/app/shared/models/edge.models.ts +++ b/ui-ngx/src/app/shared/models/edge.models.ts @@ -190,3 +190,15 @@ export enum EdgeInstructionsMethod { } export const edgeVersionAttributeKey = 'edgeVersion'; + +export enum EdgeConnectionEvent { + CONNECTED= 'CONNECTED', + DISCONNECTED = 'DISCONNECTED' +} + +export const EdgeConnectionEventTranslationMap = new Map( + [ + [EdgeConnectionEvent.CONNECTED, 'edge.connected'], + [EdgeConnectionEvent.DISCONNECTED, 'edge.disconnected'] + ] +); diff --git a/ui-ngx/src/app/shared/models/limited-api.models.ts b/ui-ngx/src/app/shared/models/limited-api.models.ts index 714441f0b2..d0e2e0cb76 100644 --- a/ui-ngx/src/app/shared/models/limited-api.models.ts +++ b/ui-ngx/src/app/shared/models/limited-api.models.ts @@ -24,7 +24,9 @@ export enum LimitedApi { WS_UPDATES_PER_SESSION = 'WS_UPDATES_PER_SESSION', CASSANDRA_QUERIES = 'CASSANDRA_QUERIES', TRANSPORT_MESSAGES_PER_TENANT = 'TRANSPORT_MESSAGES_PER_TENANT', - TRANSPORT_MESSAGES_PER_DEVICE = 'TRANSPORT_MESSAGES_PER_DEVICE' + TRANSPORT_MESSAGES_PER_DEVICE = 'TRANSPORT_MESSAGES_PER_DEVICE', + EDGE_EVENTS = 'EDGE_EVENTS', + EDGE_EVENTS_PER_EDGE = 'EDGE_EVENTS_PER_EDGE' } export const LimitedApiTranslationMap = new Map( @@ -38,6 +40,8 @@ export const LimitedApiTranslationMap = new Map( [LimitedApi.WS_UPDATES_PER_SESSION, 'api-limit.ws-updates-per-session'], [LimitedApi.CASSANDRA_QUERIES, 'api-limit.cassandra-queries'], [LimitedApi.TRANSPORT_MESSAGES_PER_TENANT, 'api-limit.transport-messages'], - [LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE, 'api-limit.transport-messages-per-device'] + [LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE, 'api-limit.transport-messages-per-device'], + [LimitedApi.EDGE_EVENTS, 'api-limit.edge-events'], + [LimitedApi.EDGE_EVENTS_PER_EDGE, 'api-limit.edge-events-per-edge'], ] ); diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 185d9006db..2cc7d8547b 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -521,7 +521,9 @@ export enum NotificationType { API_USAGE_LIMIT = 'API_USAGE_LIMIT', NEW_PLATFORM_VERSION = 'NEW_PLATFORM_VERSION', RULE_NODE = 'RULE_NODE', - RATE_LIMITS = 'RATE_LIMITS' + RATE_LIMITS = 'RATE_LIMITS', + EDGE_CONNECTION = 'EDGE_CONNECTION', + EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE' } export const NotificationTypeIcons = new Map([ @@ -632,6 +634,18 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -659,6 +675,8 @@ export const TriggerTypeTranslationMap = new Map([ [TriggerType.API_USAGE_LIMIT, 'notification.trigger.api-usage-limit'], [TriggerType.NEW_PLATFORM_VERSION, 'notification.trigger.new-platform-version'], [TriggerType.RATE_LIMITS, 'notification.trigger.rate-limits'], + [TriggerType.EDGE_CONNECTION, 'notification.trigger.edge-connection'], + [TriggerType.EDGE_COMMUNICATION_FAILURE, 'notification.trigger.edge-communication-failure'] ]); export interface NotificationUserSettings { diff --git a/ui-ngx/src/app/shared/models/rpc-widget-settings.models.ts b/ui-ngx/src/app/shared/models/rpc-widget-settings.models.ts deleted file mode 100644 index b912361805..0000000000 --- a/ui-ngx/src/app/shared/models/rpc-widget-settings.models.ts +++ /dev/null @@ -1,125 +0,0 @@ -/// -/// Copyright © 2016-2024 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 { AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { BackgroundSettings } from '@shared/models/widget-settings.models'; - -export enum RpcInitialStateAction { - DO_NOTHING = 'DO_NOTHING', - EXECUTE_RPC = 'EXECUTE_RPC', - GET_ATTRIBUTE = 'GET_ATTRIBUTE', - GET_TIME_SERIES = 'GET_TIME_SERIES' -} - -export const rpcInitialStateActions = Object.keys(RpcInitialStateAction) as RpcInitialStateAction[]; - -export const rpcInitialStateTranslations = new Map( - [ - [RpcInitialStateAction.DO_NOTHING, 'widgets.rpc-state.do-nothing'], - [RpcInitialStateAction.EXECUTE_RPC, 'widgets.rpc-state.execute-rpc'], - [RpcInitialStateAction.GET_ATTRIBUTE, 'widgets.rpc-state.get-attribute'], - [RpcInitialStateAction.GET_TIME_SERIES, 'widgets.rpc-state.get-time-series'] - ] -); - -export interface RpcSettings { - method: string; - requestTimeout: number; - requestPersistent: boolean; - persistentPollingInterval: number; -} - -export interface RpcTelemetrySettings { - key: string; -} - -export interface RpcGetAttributeSettings extends RpcTelemetrySettings { - scope: AttributeScope | null; -} - -export interface RpcSetAttributeSettings extends RpcTelemetrySettings { - scope: AttributeScope.SERVER_SCOPE | AttributeScope.SHARED_SCOPE; -} - -export enum RpcDataToStateType { - NONE = 'NONE', - FUNCTION = 'FUNCTION' -} - -export interface RpcDataToStateSettings { - type: RpcDataToStateType; - dataToStateFunction: string; - compareToValue?: any; -} - -export interface RpcActionSettings { - actionLabel?: string; -} - -export interface RpcInitialStateSettings extends RpcActionSettings { - action: RpcInitialStateAction; - defaultValue: V; - executeRpc: RpcSettings; - getAttribute: RpcGetAttributeSettings; - getTimeSeries: RpcTelemetrySettings; - dataToState: RpcDataToStateSettings; -} - -export enum RpcUpdateStateAction { - EXECUTE_RPC = 'EXECUTE_RPC', - SET_ATTRIBUTE = 'SET_ATTRIBUTE', - ADD_TIME_SERIES = 'ADD_TIME_SERIES' -} - -export const rpcUpdateStateActions = Object.keys(RpcUpdateStateAction) as RpcUpdateStateAction[]; - -export const rpcUpdateStateTranslations = new Map( - [ - [RpcUpdateStateAction.EXECUTE_RPC, 'widgets.rpc-state.execute-rpc'], - [RpcUpdateStateAction.SET_ATTRIBUTE, 'widgets.rpc-state.set-attribute'], - [RpcUpdateStateAction.ADD_TIME_SERIES, 'widgets.rpc-state.add-time-series'] - ] -); - -export enum RpcStateToParamsType { - CONSTANT = 'CONSTANT', - FUNCTION = 'FUNCTION', - NONE = 'NONE' -} - -export interface RpcStateToParamsSettings { - type: RpcStateToParamsType; - constantValue: any; - stateToParamsFunction: string; -} - -export interface RpcUpdateStateSettings extends RpcActionSettings { - action: RpcUpdateStateAction; - executeRpc: RpcSettings; - setAttribute: RpcSetAttributeSettings; - putTimeSeries: RpcTelemetrySettings; - stateToParams: RpcStateToParamsSettings; -} - -export interface RpcStateBehaviourSettings { - initialState: RpcInitialStateSettings; - updateStateByValue: (value: V) => RpcUpdateStateSettings; -} - -export interface RpcStateWidgetSettings { - initialState: RpcInitialStateSettings; - background: BackgroundSettings; -} diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index a3d5b57954..5bd9e26f13 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -15,7 +15,14 @@ /// import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, parseFunction } from '@core/utils'; -import { DataEntry, DataKey, Datasource, DatasourceData } from '@shared/models/widget.models'; +import { + DataEntry, + DataKey, + Datasource, + DatasourceData, + DatasourceType, + TargetDevice, TargetDeviceType +} from '@shared/models/widget.models'; import { Injector } from '@angular/core'; import { DatePipe } from '@angular/common'; import { DateAgoPipe } from '@shared/pipe/date-ago.pipe'; @@ -204,7 +211,7 @@ export const cssSizeToStrSize = (size?: number, unit?: cssUnit): string => (isDe export const resolveCssSize = (strSize?: string): [number, cssUnit] => { if (!strSize || !strSize.trim().length) { - return [0, 'px']; + return [null, 'px']; } let resolvedUnit: cssUnit; let resolvedSize = strSize; @@ -218,7 +225,7 @@ export const resolveCssSize = (strSize?: string): [number, cssUnit] => { resolvedSize = strSize.substring(0, strSize.length - resolvedUnit.length); } resolvedUnit = resolvedUnit || 'px'; - let numericSize = 0; + let numericSize: number = null; if (isNumeric(resolvedSize)) { numericSize = Number(resolvedSize); } @@ -600,6 +607,24 @@ export const updateDataKeyByLabel = (datasources: Datasource[], dataKey: DataKey } }; +export const getTargetDeviceFromDatasources = (datasources?: Datasource[]): TargetDevice => { + if (datasources && datasources.length) { + const datasource = datasources[0]; + if (datasource?.type === DatasourceType.device) { + return { + type: TargetDeviceType.device, + deviceId: datasource?.deviceId + }; + } else if (datasource?.type === DatasourceType.entity) { + return { + type: TargetDeviceType.entity, + entityAliasId: datasource?.entityAliasId + }; + } + } + return null; +}; + export const getAlarmFilterConfig = (datasources?: Datasource[]): AlarmFilterConfig => { if (datasources && datasources.length) { const config = datasources[0].alarmFilterConfig; diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 8d7452da89..5f4ca8f7be 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -180,6 +180,7 @@ export interface WidgetTypeParameters { previewWidth?: string; previewHeight?: string; embedTitlePanel?: boolean; + overflowVisible?: boolean; hideDataSettings?: boolean; defaultDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; defaultLatestDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; @@ -410,6 +411,23 @@ export interface Datasource { [key: string]: any; } +export const datasourceValid = (datasource: Datasource): boolean => { + const type: DatasourceType = datasource?.type; + if (type) { + switch (type) { + case DatasourceType.function: + case DatasourceType.alarmCount: + return true; + case DatasourceType.device: + return !!datasource.deviceId; + case DatasourceType.entity: + case DatasourceType.entityCount: + return !!datasource.entityAliasId; + } + } + return false; +}; + export enum TargetDeviceType { device = 'device', entity = 'entity' @@ -520,7 +538,8 @@ export enum WidgetActionType { openDashboard = 'openDashboard', custom = 'custom', customPretty = 'customPretty', - mobileAction = 'mobileAction' + mobileAction = 'mobileAction', + openURL = 'openURL' } export enum WidgetMobileActionType { @@ -541,7 +560,8 @@ export const widgetActionTypeTranslationMap = new Map( [ WidgetActionType.openDashboard, 'widget-action.open-dashboard' ], [ WidgetActionType.custom, 'widget-action.custom' ], [ WidgetActionType.customPretty, 'widget-action.custom-pretty' ], - [ WidgetActionType.mobileAction, 'widget-action.mobile-action' ] + [ WidgetActionType.mobileAction, 'widget-action.mobile-action' ], + [ WidgetActionType.openURL, 'widget-action.open-URL' ] ] ); @@ -632,11 +652,7 @@ export interface CustomActionDescriptor { customModules?: Type[]; } -export interface WidgetActionDescriptor extends CustomActionDescriptor { - id: string; - name: string; - icon: string; - displayName?: string; +export interface WidgetAction extends CustomActionDescriptor { type: WidgetActionType; targetDashboardId?: string; targetDashboardStateId?: string; @@ -657,10 +673,37 @@ export interface WidgetActionDescriptor extends CustomActionDescriptor { setEntityId?: boolean; stateEntityParamName?: string; mobileAction?: WidgetMobileActionDescriptor; + url?: string; +} + +export interface WidgetActionDescriptor extends WidgetAction { + id: string; + name: string; + icon: string; + displayName?: string; useShowWidgetActionFunction?: boolean; showWidgetActionFunction?: string; } +export const actionDescriptorToAction = (descriptor: WidgetActionDescriptor): WidgetAction => { + const result: WidgetActionDescriptor = {...descriptor}; + delete result.id; + delete result.name; + delete result.icon; + delete result.displayName; + delete result.useShowWidgetActionFunction; + delete result.showWidgetActionFunction; + return result; +}; + +export const defaultWidgetAction = (setEntityId = true): WidgetAction => ({ + type: WidgetActionType.updateDashboardState, + targetDashboardStateId: null, + openRightLayout: false, + setEntityId, + stateEntityParamName: null + }); + export interface WidgetComparisonSettings { comparisonEnabled?: boolean; timeForComparison?: moment_.unitOfTime.DurationConstructor; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 0abf2167f6..71f121ab17 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -217,6 +217,7 @@ import { MultipleGalleryImageInputComponent } from '@shared/components/image/mul import { EmbedImageDialogComponent } from '@shared/components/image/embed-image-dialog.component'; import { ImageGalleryDialogComponent } from '@shared/components/image/image-gallery-dialog.component'; import { RuleChainSelectPanelComponent } from '@shared/components/rule-chain/rule-chain-select-panel.component'; +import { WidgetButtonComponent } from '@shared/components/button/widget-button.component'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -414,7 +415,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) GalleryImageInputComponent, MultipleGalleryImageInputComponent, EmbedImageDialogComponent, - ImageGalleryDialogComponent + ImageGalleryDialogComponent, + WidgetButtonComponent ], imports: [ CommonModule, @@ -666,7 +668,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) GalleryImageInputComponent, MultipleGalleryImageInputComponent, EmbedImageDialogComponent, - ImageGalleryDialogComponent + ImageGalleryDialogComponent, + WidgetButtonComponent ] }) export class SharedModule { } diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md new file mode 100644 index 0000000000..712f2b45e7 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md @@ -0,0 +1,57 @@ +#### Edge communication failure notification templatization + +
+
+ +Notification subject and message fields support templatization. +The list of available templatization parameters depends on the template type. +See the available types and parameters below: + +Available template parameters: + +* `edgeId` - the edge id as uuid string; +* `edgeName` - the name of the edge; +* `failureMsg` - the string representation of the failure, occurred on the Edge; + +Parameter names must be wrapped using `${...}`. For example: `${edgeName}`. +You may also modify the value of the parameter with one of the suffixes: + +* `upperCase`, for example - `${edgeName:upperCase}` +* `lowerCase`, for example - `${edgeName:lowerCase}` +* `capitalize`, for example - `${edgeName:capitalize}` + +
+ +##### Examples + +Let's assume the notification about the failing of processing connection to Edge. +The following template: + +```text +Edge '${edgeName}' communication failure occurred +{:copy-code} +``` + +will be transformed to: + +```text +Edge 'DatacenterEdge' communication failure occurred +``` + +
+ +The following template: + +```text +Failure message: '${failureMsg}' +{:copy-code} +``` + +will be transformed to: + +```text +Failure message: 'Failed to process edge connection!' +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_connection.md b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md new file mode 100644 index 0000000000..37f0ec7573 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md @@ -0,0 +1,44 @@ +#### Edge connection notification templatization + +
+
+ +Notification subject and message fields support templatization. +The list of available templatization parameters depends on the template type. +See the available types and parameters below: + +Available template parameters: + +* `edgeId` - the edge id as uuid string; +* `edgeName` - the name of the edge; +* `eventType` - the string representation of the connectivity status: connected or disconnected; + +Parameter names must be wrapped using `${...}`. For example: `${edgeName}`. +You may also modify the value of the parameter with one of the suffixes: + +* `upperCase`, for example - `${edgeName:upperCase}` +* `lowerCase`, for example - `${edgeName:lowerCase}` +* `capitalize`, for example - `${edgeName:capitalize}` + +
+ +##### Examples + +Let's assume the notification about the connecting Edge into the ThingsBoard. +The following template: + +```text +Edge '${edgeName}' is now ${eventType} +{:copy-code} +``` + +will be transformed to: + +```text +Edge 'DatacenterEdge' is now connected +``` + +
+ +
+
diff --git a/ui-ngx/src/assets/help/en_US/notification/rate_limits.md b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md index 8f6319ad80..d314e681c2 100644 --- a/ui-ngx/src/assets/help/en_US/notification/rate_limits.md +++ b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md @@ -11,7 +11,7 @@ Available template parameters: * `api` - rate-limited API label; one of: 'REST API requests', 'REST API requests per customer', 'transport messages', 'transport messages per device', 'Cassandra queries', 'WS updates per session', 'notification requests', 'notification requests per rule', - 'entity version creation', 'entity version load'; + 'entity version creation', 'entity version load', 'Edge events', 'Edge events per edge'; * `limitLevelEntityType` - entity type of the limit level entity, e.g. 'Tenant', 'Device', 'Notification rule', 'Customer', etc.; * `limitLevelEntityId` - id of the limit level entity; * `limitLevelEntityName` - name of the limit level entity; diff --git a/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md index 0ccbbd9058..50301746c4 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md @@ -1,4 +1,4 @@ Fields templatization feature allows you to process the incoming messages with dynamic configuration by substitution of templates specified in the configuration fields with values from message or message metadata. -For more detailed information, please refer to the ThingsBoard [documentation](https://thingsboard.io/docs/user-guide/templatization/) +For more detailed information, please refer to the ThingsBoard [documentation](${siteBaseUrl}/docs/user-guide/templatization/) diff --git a/ui-ngx/src/assets/help/en_US/rulenode/common_node_script_args.md b/ui-ngx/src/assets/help/en_US/rulenode/common_node_script_args.md index 296e5b735c..d9b5bdb0b7 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/common_node_script_args.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/common_node_script_args.md @@ -8,4 +8,4 @@ Enable 'debug mode' for your rule node to see the messages that arrive in near real-time. -See Debugging for more information. \ No newline at end of file +See Debugging for more information. diff --git a/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md b/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md index a31eae72cf..0f6ab96736 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/switch_node_script_fn.md @@ -15,7 +15,7 @@ JavaScript function computing **an array of Link names** to forward the incoming Should return an array of `string` values presenting **link names** that the Rule Engine should use to further route the incoming Message.
If the result is an empty array - message will not be routed to any Node and will be immediately -acknowledged. +acknowledged.
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/tbel/common_node_script_args.md b/ui-ngx/src/assets/help/en_US/rulenode/tbel/common_node_script_args.md index 296e5b735c..d9b5bdb0b7 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/tbel/common_node_script_args.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/tbel/common_node_script_args.md @@ -8,4 +8,4 @@ Enable 'debug mode' for your rule node to see the messages that arrive in near real-time. -See Debugging for more information. \ No newline at end of file +See Debugging for more information. diff --git a/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md b/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md index 7fd3b7447b..aed694848e 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/tbel/switch_node_script_fn.md @@ -15,7 +15,7 @@ Should return an array of `string` values presenting **link names** that the Rule Engine should use to further route the incoming Message.
If the result is an empty array - message will not be routed to any Node and will be immediately -acknowledged. +acknowledged.
diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index db7d6b06ce..d530dae430 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -4584,8 +4584,8 @@ "popover-placement-leftBottom": "Esquerra inferior", "popover-hide-on-click-outside": "Amaga la finestra emergent al clic exterior", "popover-hide-dashboard-toolbar": "Amaga la barra d'eines del tauler a la finestra emergent", - "popover-width": "Amplada emergent en unitats del navegador (p. ex. 100 píxels, 25 vw)", - "popover-height": "Alçada emergent en unitats del navegador (p. ex. 100px, 25vh)", + "popover-width": "Amplada emergent", + "popover-height": "Alçada emergent", "popover-style": "Estil popover", "open-new-browser-tab": "Obrir en una nova pestanya", "mobile": { @@ -4681,7 +4681,7 @@ "action-source-required": "Cal origen de acció.", "action-name": "Nom", "action-name-required": "Cal nom de acció.", - "action-name-not-unique": "Existe una acció amb el mateix nom.
El nom d'acció ha de ser únic dins de la mateixa font d'acció (origen).", + "action-name-not-unique": "Existe una acció amb el mateix nom.\nEl nom d'acció ha de ser únic dins de la mateixa font d'acció (origen).", "action-icon": "Icona", "show-hide-action-using-function": "Mostra/amaga l'acció mitjançant la funció", "action-type": "Tipus", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 7402aedc5c..fa23dddb45 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2931,7 +2931,7 @@ "action-source-required": "Zdroj akce je povinný.", "action-name": "Název", "action-name-required": "Název akce je povinný.", - "action-name-not-unique": "Jiná akce s identickým názvem již existuje.
Název akce by měl být v rámci zdroje akce unikátní.", + "action-name-not-unique": "Jiná akce s identickým názvem již existuje.\nNázev akce by měl být v rámci zdroje akce unikátní.", "action-icon": "Ikona", "action-type": "Typ", "action-type-required": "Typ akce je povinný.", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index e815ac65c6..7a1cea02a3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -3439,7 +3439,7 @@ "action-source-required": "Handlingskilde er påkrævet.", "action-name": "Navn", "action-name-required": "Handlingsnavn er påkrævet.", - "action-name-not-unique": "Der findes allerede en anden handling med samme navn.
Handlingsnavnet skal være unikt inden for den samme handlingskilde.", + "action-name-not-unique": "Der findes allerede en anden handling med samme navn.\nHandlingsnavnet skal være unikt inden for den samme handlingskilde.", "action-icon": "Ikon", "action-type": "Type", "action-type-required": "Handlingstype er påkrævet.", diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index 582e3e2902..5a2a7cbeeb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -67,10 +67,10 @@ "next-with-label": "Nächste: {{label}}", "read-more": "Mehr dazu", "hide": "Verstecken", - "done": "Erledigt", - "print": "Drucken", - "restore": "Wiederherstellen", - "confirm": "Bestätigen", + "done": "Erledigt", + "print": "Drucken", + "restore": "Wiederherstellen", + "confirm": "Bestätigen", "more": "Mehr", "less": "Weniger", "skip": "Überspringen", @@ -107,6 +107,23 @@ "base-url-required": "Basis-URL ist erforderlich.", "prohibit-different-url": "Prohibit to use hostname from the client request headers", "prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled", + "device-connectivity": { + "device-connectivity": "Geräte Konnektivität", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "Falls Host und Port leer sind, werden die Standardwerte des Protokolls verwendet", + "host": "Host", + "port": "Port", + "port-pattern": "Port muss einen positiven Wert haben", + "port-range": "Port sollte im Bereich 1 und 65535 liegen." + }, "mail-from": "E-Mail von", "mail-from-required": "E-Mail von ist erforderlich.", "smtp-protocol": "SMTP Protokoll", @@ -138,7 +155,16 @@ "mail-template": "E-Mail Vorlage", "test": "E-Mail Nachricht testen", "activation": "Nachricht Benutzerkontoaktivierung", - "account-activated": "Nachricht Benutzerkonto aktiviert" + "account-activated": "Nachricht Benutzerkonto aktiviert", + "account-lockout": "Nachricht zur Kontosperrung", + "reset-password": "Nachricht zum Zurücksetzen des Passworts", + "password-was-reset": "Nachricht für Passwort wurde zurückgesetzt", + "user-activated": "Nachricht für Benutzerkonto wurde aktiviert", + "user-registered": "Nachricht für Benutzer hat sich registriert", + "api-usage-state-enabled": "API-Nutzungsstatus wurde aktiviert", + "api-usage-state-warning": "Warnung zum API-Nutzungsstatus", + "api-usage-state-disabled": "API-Nutzungsstatus wurde deaktiviert", + "two-fa-verification": "2FA-Bestätigungsnachricht" }, "mail-subject": "E-Mail Betreff", "mail-body": "E-Mail Nachricht", @@ -186,17 +212,17 @@ "alarm-status-list": "Alarm Statusliste", "any-status": "Jeder Status", "search-status": { - "ANY": "Jeder", - "ACTIVE": "Aktiv", - "CLEARED": "Gelöscht", - "ACK": "Bestätigt", - "UNACK": "Nicht bestätigt" + "ANY": "Jeder", + "ACTIVE": "Aktiv", + "CLEARED": "Gelöscht", + "ACK": "Bestätigt", + "UNACK": "Nicht bestätigt" }, "display-status": { - "ACTIVE_UNACK": "Nicht bestätigt aktiv", - "ACTIVE_ACK": "Bestätigt aktiv", - "CLEARED_UNACK": "Nicht bestätigt", - "CLEARED_ACK": "Bestätigung gelöscht" + "ACTIVE_UNACK": "Nicht bestätigt aktiv", + "ACTIVE_ACK": "Bestätigt aktiv", + "CLEARED_UNACK": "Nicht bestätigt", + "CLEARED_ACK": "Bestätigung gelöscht" }, "no-alarms-prompt": "Keine Alarme gefunden", "created-time": "Erstellungszeit", @@ -1959,7 +1985,7 @@ "action-source-required": "Aktionsquelle ist erforderlich.", "action-name": "Name", "action-name-required": "Aktionsname ist erforderlich.", - "action-name-not-unique": "Eine andere Aktion mit demselben Namen ist bereits vorhanden.
Der Aktionsname sollte innerhalb derselben Aktionsquelle eindeutig sein.", + "action-name-not-unique": "Eine andere Aktion mit demselben Namen ist bereits vorhanden.\n Der Aktionsname sollte innerhalb derselben Aktionsquelle eindeutig sein.", "action-icon": "Symbol ", "action-type": "Art", "action-type-required": "Aktionsart ist erforderlich.", diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index 0594dd5853..eea0ea4377 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -2430,7 +2430,7 @@ "action-source-required": "Απαιτείται πηγή ενέργειας.", "action-name": "Όνομα", "action-name-required": "Απαιτείται όνομα ενέργειας.", - "action-name-not-unique": "Μια άλλη ενέργεια με το ίδιο όνομα υπάρχει ήδη.
Το όνομα ενέργειας πρέπει να είναι μοναδικό μέσα στην ίδια πηγή ενέργειας.", + "action-name-not-unique": "Μια άλλη ενέργεια με το ίδιο όνομα υπάρχει ήδη.\nΤο όνομα ενέργειας πρέπει να είναι μοναδικό μέσα στην ίδια πηγή ενέργειας.", "action-icon": "Εικονίδιο", "action-type": "Τύπος", "action-type-required": "Απαιτείται τύπος ενέργειας.", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index da8972c39e..631d40398c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -859,7 +859,9 @@ "rest-api-requests-per-customer": "REST API requests per customer", "transport-messages": "Transport messages", "transport-messages-per-device": "Transport messages per device", - "ws-updates-per-session": "WS updates per session" + "ws-updates-per-session": "WS updates per session", + "edge-events": "Edge events", + "edge-events-per-edge": "Edge events per edge" }, "audit-log": { "audit": "Audit", @@ -1009,6 +1011,10 @@ "edges": "Customer edge instances", "manage-edges": "Manage edges" }, + "css-size": { + "size-value-required": "Size value is required", + "invalid-size-value": "Invalid size value" + }, "date": { "last-update-n-ago": "Last update N ago", "last-update-n-ago-text": "Last update {{ agoText }}", @@ -2036,7 +2042,9 @@ "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge.

List of missing rule chain(s):
{{missingRuleChains}}", "upgrade-instructions": "Upgrade Instructions", - "widget-datasource-error": "This widget supports only EDGE entity datasource" + "widget-datasource-error": "This widget supports only EDGE entity datasource", + "connected": "Connected", + "disconnected": "Disconnected" }, "edge-event": { "type-dashboard": "Dashboard", @@ -3290,6 +3298,8 @@ "device-list-rule-hint": "If the field is empty, the trigger will be applied to all devices", "device-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all device profiles", "disabled": "Disabled", + "edge-trigger-settings": "Edge trigger settings", + "edge-list-rule-hint": "If the field is empty, the trigger will be applied to all edge instances", "edit-notification-recipients-group": "Edit notification recipients group", "edit-notification-template": "Edit notification template", "edit-rule": "Edit rule", @@ -3436,7 +3446,9 @@ "rule-engine-lifecycle-event": "Rule engine lifecycle event", "rule-node": "Rule node", "new-platform-version": "New platform version", - "rate-limits": "Exceeded rate limits" + "rate-limits": "Exceeded rate limits", + "edge-communication-failure": "Edge communication failure", + "edge-connection": "Edge connection" }, "templates": "Templates", "notification-templates": "Notifications / Templates", @@ -3457,6 +3469,8 @@ "rule-engine-lifecycle-event": "Rule engine lifecycle event", "new-platform-version": "New platform version", "rate-limits": "Exceeded rate limits", + "edge-connection": "Edge connection", + "edge-communication-failure": "Edge communication failure", "trigger": "Trigger", "trigger-required": "Trigger is required" }, @@ -4189,6 +4203,10 @@ "edit-tenant-entity-import-rate-limit-title": "Edit entity version load rate limits", "edit-tenant-notification-request-rate-limit-title": "Edit notification requests rate limits", "edit-tenant-notification-requests-per-rule-rate-limit-title": "Edit notification requests per notification rule rate limits", + "edit-edge-events-rate-limit": "Edit edge events rate limits", + "edit-edge-events-per-edge-rate-limit": "Edit edge events per edge rate limits", + "edge-events-rate-limit": "Edge events", + "edge-events-per-edge-rate-limit": "Edge events per edge", "messages-per": "messages per", "not-set": "Not set", "number-of-messages": "Number of messages", @@ -4995,6 +5013,8 @@ "target-dashboard-state-required": "Target dashboard state is required", "set-entity-from-widget": "Set entity from widget", "target-dashboard": "Target dashboard", + "select-target-dashboard": "Select target dashboard", + "target-dashboard-required": "Target dashboard is required.", "open-right-layout": "Open right dashboard layout (mobile view)", "state-display-type": "Dashboard state display option", "open-normal": "Normal", @@ -5020,12 +5040,16 @@ "popover-placement-leftBottom": "Left bottom", "popover-hide-on-click-outside": "Hide popover on outside click", "popover-hide-dashboard-toolbar": "Hide dashboard toolbar in popover", - "popover-width": "Popover width in browser units (ex. 100px, 25vw)", - "popover-height": "Popover height in browser units (ex. 100px, 25vh)", + "popover-width": "Popover width", + "popover-height": "Popover height", "popover-style": "Popover style", "open-new-browser-tab": "Open in a new browser tab", + "open-URL": "Open URL", + "URL": "URL", + "url-required": "URL is required.", "mobile": { "action-type": "Mobile action type", + "select-action-type": "Select mobile action type", "action-type-required": "Mobile action type is required", "take-picture-from-gallery": "Take picture from gallery", "take-photo": "Take photo", @@ -5035,7 +5059,9 @@ "make-phone-call": "Make phone call", "get-location": "Get phone location", "take-screenshot": "Take screenshot" - } + }, + "custom-action-function": "Custom action function", + "custom-pretty-function": "Custom action (with HTML template) function" }, "widgets-bundle": { "current": "Current bundle", @@ -5131,12 +5157,14 @@ "search-actions": "Search actions", "no-actions-text": "No actions found", "action-source": "Action source", + "select-action-source": "Select action source", "action-source-required": "Action source is required.", "action-name": "Name", "action-name-required": "Action name is required.", - "action-name-not-unique": "Another action with the same name already exists.
Action name should be unique within the same action source.", + "action-name-not-unique": "Another action with the same name already exists.\nAction name should be unique within the same action source.", "action-icon": "Icon", "show-hide-action-using-function": "Show/hide action using function", + "show-action-function": "Show action function", "action-type": "Type", "action-type-required": "Action type is required.", "edit-action": "Edit action", @@ -5179,6 +5207,69 @@ "invalid-widget-file-error": "Unable to import widget: Invalid widget data structure." }, "widgets": { + "action-button": { + "behavior": "Behavior", + "on-click": "On click", + "on-click-hint": "Action triggered when the button is clicked" + }, + "command-button": { + "behavior": "Behavior", + "on-click": "On click", + "on-click-hint": "Action performed when the button is clicked." + }, + "power-button": { + "behavior": "Behavior", + "power-on": "Power 'On'", + "power-on-hint": "Action performed to power ON the component.", + "power-off": "Power 'Off'", + "power-off-hint": "Action performed to power OFF the component.", + "on-label": "On", + "off-label": "Off", + "layout": "Layout", + "layout-default": "Default", + "layout-simplified": "Simplified", + "layout-outlined": "Outlined", + "layout-default-volume": "Default.Volume", + "layout-simplified-volume": "Simplified.Volume", + "layout-outlined-volume": "Outlined.Volume", + "main": "Main", + "background": "Background", + "power-on-colors": "Power 'On' colors", + "power-off-colors": "Power 'Off' colors", + "disabled-colors": "Disabled colors", + "button": "Button" + }, + "button": { + "layout": "Layout", + "outlined": "Outlined", + "filled": "Filled", + "underlined": "Underlined", + "basic": "Basic", + "auto-scale": "Auto scale", + "label": "Label", + "icon": "Icon", + "color-palette": "Color palette", + "main": "Main", + "background": "Background", + "custom-styles": "Custom styles", + "clear-style": "Clear style", + "shadow": "Shadow", + "enabled": "Enabled", + "disabled": "Disabled", + "preview": "Preview", + "copy-style-from": "Copy style from" + }, + "button-state": { + "activated-state": "Activated state", + "activated-state-hint": "Configure condition under which the button is active.", + "disabled-state": "Disabled state", + "disabled-state-hint": "Configure condition under which the button is disabled.", + "enabled": "Enabled", + "hovered": "Hovered", + "pressed": "Pressed", + "activated": "Activated", + "disabled": "Disabled" + }, "background": { "background": "Background", "background-settings": "Background settings", @@ -6008,60 +6099,6 @@ "min-value": "Minimum value", "max-value": "Maximum value" }, - "rpc-state": { - "initial-state": "Initial state", - "initial-state-hint": "Action to get the initial value of the component.", - "turn-on": "Turn 'On'", - "turn-on-hint": "Action performed to turn ON the component.", - "turn-off": "Turn 'Off'", - "turn-off-hint": "Action performed to turn OFF the component.", - "on": "On", - "off": "Off", - "do-nothing": "Do nothing", - "execute-rpc": "Execute RPC", - "get-attribute": "Get attribute", - "set-attribute": "Set attribute", - "get-time-series": "Get time-series", - "add-time-series": "Add time-series", - "execute-rpc-text": "Execute RPC method '{{methodName}}'", - "get-attribute-text": "Use attribute '{{key}}'", - "get-time-series-text": "Use time-series '{{key}}'", - "set-attribute-to-value-text": "Set '{{key}}' attribute to: {{value}}", - "add-time-series-value-text": "Add '{{key}}' time-series value: {{value}}", - "set-attribute-text": "Set '{{key}}' attribute", - "add-time-series-text": "Add '{{key}}' time-series", - "action": "Action", - "value": "Value", - "init-value-hint": "Value that will be set until device sends data.", - "method": "Method", - "method-name-required": "Method name is required.", - "request-timeout-ms": "RPC request timeout (ms)", - "request-timeout-required": "Request timeout is required.", - "min-request-timeout-error": "Request timeout value should be greater or equal 5000 ms (5 seconds).", - "request-persistent": "RPC request persistent", - "persistent-polling-interval": "Persistent polling interval (ms)", - "persistent-polling-interval-hint": "Polling interval (ms) to get persistent RPC command response", - "persistent-polling-interval-required": "Persistent polling interval is required.", - "min-persistent-polling-interval-error": "Persistent polling interval value should be greater or equal 1000 ms (1 second).", - "attribute-scope": "Attribute scope", - "attribute-key": "Attribute key", - "attribute-key-required": "Attribute key is required.", - "time-series-key": "Time-series key", - "time-series-key-required": "Time-series key is required.", - "action-result-converter": "Action result converter", - "converter-none": "None", - "converter-function": "Function", - "converter-constant": "Constant", - "parse-value-function": "Parse value function", - "on-when-result-is": "'On' when result is", - "parameters": "Parameters", - "convert-value-function": "Convert value function", - "error": { - "target-entity-is-not-set": "Target entity is not set!", - "failed-to-perform-action": "Failed to perform the {{ actionLabel }} action.", - "invalid-attribute-scope": "{{scope}} attribute scope is not supported by {{entityType}} entity." - } - }, "maps": { "select-entity": "Select entity", "select-entity-hint": "Hint: after selection click at the map to set position", @@ -6313,6 +6350,31 @@ "off-label": "Off label", "switch": "Switch" }, + "slider": { + "behavior": "Behavior", + "initial-value": "Initial value", + "initial-value-hint": "Action to get the initial value of the slider.", + "on-value-change": "On value change", + "on-value-change-hint": "Action triggered when the slider value is changed.", + "layout": "Layout", + "layout-default": "Default", + "layout-extended": "Extended", + "layout-simplified": "Simplified", + "auto-scale": "Auto scale", + "icon": "Icon", + "value": "Value", + "range": "Range", + "min": "min", + "max": "max", + "range-ticks": "Range ticks", + "tick-marks": "Tick marks", + "colors": "Colors", + "main": "Main", + "background": "Background", + "left-icon": "Left icon", + "right-icon": "Right icon", + "slider": "Slider" + }, "value-card": { "layout": "Layout", "layout-square": "Square", @@ -6335,6 +6397,7 @@ "layout": "Layout", "background-overlay": "Value background overlay", "total-volume": "Total volume", + "total-volume-units": "Total volume units", "tank": "Tank", "shape": "Shape", "datasource-units": "Source units", @@ -6534,6 +6597,66 @@ "source-entity-alias": "Source entity alias", "source-entity-attribute": "Source entity attribute" }, + "rpc-state": { + "initial-state": "Initial state", + "initial-state-hint": "Action to get the initial state (On/Off) of the component.", + "disabled-state": "Disabled state", + "disabled-state-hint": "Configure condition under which the component is disabled.", + "turn-on": "Turn 'On'", + "turn-on-hint": "Action triggered when the slider is switched to 'On'", + "turn-off": "Turn 'Off'", + "turn-off-hint": "Action triggered when the slider is switched to 'Off'", + "on": "On", + "off": "Off", + "disabled": "Disabled" + }, + "value-action": { + "do-nothing": "Do nothing", + "execute-rpc": "Execute RPC", + "get-attribute": "Get attribute", + "set-attribute": "Set attribute", + "get-time-series": "Get time-series", + "add-time-series": "Add time-series", + "execute-rpc-text": "Execute RPC method '{{methodName}}'", + "get-attribute-text": "Use attribute '{{key}}'", + "get-time-series-text": "Use time-series '{{key}}'", + "set-attribute-to-value-text": "Set '{{key}}' attribute to: {{value}}", + "add-time-series-value-text": "Add '{{key}}' time-series value: {{value}}", + "set-attribute-text": "Set '{{key}}' attribute", + "add-time-series-text": "Add '{{key}}' time-series", + "action": "Action", + "value": "Value", + "init-value-hint": "Value that will be set until device sends data.", + "method": "Method", + "method-name-required": "Method name is required.", + "request-timeout-ms": "RPC request timeout (ms)", + "request-timeout-required": "Request timeout is required.", + "min-request-timeout-error": "Request timeout value should be greater or equal 5000 ms (5 seconds).", + "request-persistent": "RPC request persistent", + "persistent-polling-interval": "Persistent polling interval (ms)", + "persistent-polling-interval-hint": "Polling interval (ms) to get persistent RPC command response", + "persistent-polling-interval-required": "Persistent polling interval is required.", + "min-persistent-polling-interval-error": "Persistent polling interval value should be greater or equal 1000 ms (1 second).", + "attribute-scope": "Attribute scope", + "attribute-key": "Attribute key", + "attribute-key-required": "Attribute key is required.", + "time-series-key": "Time-series key", + "time-series-key-required": "Time-series key is required.", + "action-result-converter": "Action result converter", + "converter-none": "None", + "converter-function": "Function", + "converter-constant": "Constant", + "converter-value": "Value", + "parse-value-function": "Parse value function", + "state-when-result-is": "'{{state}}' when result is", + "parameters": "Parameters", + "convert-value-function": "Convert value function", + "error": { + "target-entity-is-not-set": "Target entity is not set!", + "failed-to-perform-action": "Failed to perform the {{ actionLabel }} action.", + "invalid-attribute-scope": "{{scope}} attribute scope is not supported by {{entityType}} entity." + } + }, "widget-font": { "font-settings": "Font settings", "font-family": "Font family", @@ -6780,7 +6903,8 @@ "element-click": "On HTML element click", "pie-slice-click": "On slice click", "row-double-click": "On row double click", - "card-click": "On card click" + "card-click": "On card click", + "click": "On click" } }, "paginator" : { @@ -6809,6 +6933,7 @@ "ko_KR": "한국어", "lv_LV": "Latviešu", "nl_BE": "Koninkrijk België", + "pl_PL": "Polski", "pt_BR": "Português do Brasil", "ro_RO": "Română", "sl_SI": "Slovenščina", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 8727339851..e80fbf6c3e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -4883,8 +4883,8 @@ "popover-placement-leftBottom": "Izquierda inferior", "popover-hide-on-click-outside": "Ocultar en click fuera del popover", "popover-hide-dashboard-toolbar": "Ocultar caja de herramientas en popover", - "popover-width": "Ancho de popover en unidades de navegador (ej. 100px, 25vw)", - "popover-height": "Altura de popover en unidades de navegador (ej. 100px, 25vh)", + "popover-width": "Ancho de popover", + "popover-height": "Altura de popover", "popover-style": "Estilo de popover", "open-new-browser-tab": "Abrir en una nueva pestaña", "mobile": { @@ -4996,7 +4996,7 @@ "action-source-required": "Origen de acción requerido.", "action-name": "Nombre", "action-name-required": "Nombre de accion requerido.", - "action-name-not-unique": "Existe una acción con el mismo nombre.
El nombre de acción debe ser único dentro de la misma fuente de acción (origen).", + "action-name-not-unique": "Existe una acción con el mismo nombre.\nEl nombre de acción debe ser único dentro de la misma fuente de acción (origen).", "action-icon": "Icono", "show-hide-action-using-function": "Mostrar/Ocultar acción usando función", "action-type": "Tipo", diff --git a/ui-ngx/src/assets/locale/locale.constant-fa_IR.json b/ui-ngx/src/assets/locale/locale.constant-fa_IR.json index 222b40042a..4ab3e8a059 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fa_IR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fa_IR.json @@ -1529,7 +1529,7 @@ "action-source-required": ".منشأ اقدام مورد نياز است", "action-name": "نام", "action-name-required": ".نام اقدام مورد نياز است", - "action-name-not-unique": ".در حيطه يک منشأ اقدام، نام اقدام بايد منحصر بفرد باشد
.در حال حاضر اقدامي ديگر با نام مشابه موجود است", + "action-name-not-unique": ".در حيطه يک منشأ اقدام، نام اقدام بايد منحصر بفرد باشد\n.در حال حاضر اقدامي ديگر با نام مشابه موجود است", "action-icon": "شمايل", "action-type": "نوع", "action-type-required": ".نوع اقدام مورد نياز است", diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index 7e678d9ed9..33201a70b6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -2145,7 +2145,7 @@ "action": "Action", "action-icon": "Icône", "action-name": "Nom", - "action-name-not-unique": "Une autre action portant le même nom existe déjà.
Le nom de l'action doit être unique dans la même source d'action.", + "action-name-not-unique": "Une autre action portant le même nom existe déjà. \n Le nom de l'action doit être unique dans la même source d'action.", "action-name-required": "Le nom de l'action est requis", "action-source": "Source de l'action", "action-source-required": "Une source d'action est requise.", diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index f2e1ffa240..9b00f36842 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -1574,7 +1574,7 @@ "action-source-required": "Sorgente azione obbligatoria.", "action-name": "Nome", "action-name-required": "Nome azione obbligatorio.", - "action-name-not-unique": "Un'altra azione con lo stesso nome è già presente.
Il nome di una azione dovrebbe essere univoco all'interno della stessa sorgente.", + "action-name-not-unique": "Un'altra azione con lo stesso nome è già presente.\nIl nome di una azione dovrebbe essere univoco all'interno della stessa sorgente.", "action-icon": "Icona", "action-type": "Tipo", "action-type-required": "Tipo azione obbligatorio.", diff --git a/ui-ngx/src/assets/locale/locale.constant-ja_JP.json b/ui-ngx/src/assets/locale/locale.constant-ja_JP.json index c65928e452..c6bd8f2396 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ja_JP.json +++ b/ui-ngx/src/assets/locale/locale.constant-ja_JP.json @@ -1415,7 +1415,7 @@ "action-source-required": "アクションソースが必要です。", "action-name": "名", "action-name-required": "アクション名は必須です。", - "action-name-not-unique": "同じ名前の別のアクションがすでに存在します。
アクション名は、同じアクションソース内で一意である必要があります。", + "action-name-not-unique": "同じ名前の別のアクションがすでに存在します。\nアクション名は、同じアクションソース内で一意である必要があります。", "action-icon": "アイコン", "action-type": "タイプ", "action-type-required": "アクションタイプが必要です。", diff --git a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json index 28458f0a70..f645cb0f31 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -1665,7 +1665,7 @@ "action-source-required": "მოქმედების წყარო საჭიროა.", "action-name": "სახელი", "action-name-required": "მოქმედების სახელი საჭიროა.", - "action-name-not-unique": "სხვა მოქმედება იგივე სახელით უკვე არსებობს.
მოქმედების სახელი უნდა იყოს უნიკალური ერთი და იგივე მონაცემთა წყაროსთვის.", + "action-name-not-unique": "სხვა მოქმედება იგივე სახელით უკვე არსებობს.\nმოქმედების სახელი უნდა იყოს უნიკალური ერთი და იგივე მონაცემთა წყაროსთვის.", "action-icon": "ხატულა", "action-type": "ტიპი", "action-type-required": "მოქმედების ტიპი საჭიროა.", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 09cea16190..d6cb014094 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -2307,7 +2307,7 @@ "action-source-required": "액션 소스를 입력하세요.", "action-name": "이름", "action-name-required": "액션 이름을 입력하세요.", - "action-name-not-unique": "같은 이름의 액션이 이미 존재합니다.
같은 액션 소스에서 액션 이름이 중복될 수 없습니다.", + "action-name-not-unique": "같은 이름의 액션이 이미 존재합니다.\n같은 액션 소스에서 액션 이름이 중복될 수 없습니다.", "action-icon": "아이콘", "action-type": "유형", "action-type-required": "액션 유형을 입력하세요.", diff --git a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json index 63e7fb6db2..0f5c946063 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -1578,7 +1578,7 @@ "action-source-required": "Aktivitāšu avoti ir nepieciešami.", "action-name": "Nosaukums", "action-name-required": "Aktitiāšu nosaukums ir nepieciešams.", - "action-name-not-unique": "Cita aktivitāte ar tādu pašu nosaukumu jau eksistē.
Aktitivātes nosaukumam ir jābūt unikālam vienā aktivitātes avotā.", + "action-name-not-unique": "Cita aktivitāte ar tādu pašu nosaukumu jau eksistē.\nAktitivātes nosaukumam ir jābūt unikālam vienā aktivitātes avotā.", "action-icon": "Ikona", "action-type": "Tips", "action-type-required": "Aktivitātes tips ir nepieciešams.", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index df3820d497..4cd3bb1e49 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -5382,8 +5382,8 @@ "popover-placement-leftBottom": "Links onder", "popover-hide-on-click-outside": "Popover verbergen bij klikken aan de buitenkant", "popover-hide-dashboard-toolbar": "Dashboardwerkbalk verbergen in pop-over", - "popover-width": "Popover-breedte in browsereenheden (bijv. 100px, 25vw)", - "popover-height": "Popover-hoogte in browsereenheden (bijv. 100px, 25vh)", + "popover-width": "Popover-breedte", + "popover-height": "Popover-hoogte", "popover-style": "Popover-stijl", "open-new-browser-tab": "Openen in een nieuw browsertabblad", "mobile": { @@ -5481,7 +5481,7 @@ "action-source-required": "Actiebron is vereist.", "action-name": "Naam", "action-name-required": "De naam van de actie is vereist.", - "action-name-not-unique": "Er bestaat al een andere actie met dezelfde naam.
De naam van de actie moet uniek zijn binnen dezelfde actiebron.", + "action-name-not-unique": "Er bestaat al een andere actie met dezelfde naam.\nDe naam van de actie moet uniek zijn binnen dezelfde actiebron.", "action-icon": "Pictogram", "show-hide-action-using-function": "Actie tonen/verbergen met behulp van functie", "action-type": "Type", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json new file mode 100644 index 0000000000..014666017c --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -0,0 +1,6703 @@ +{ + "access":{ + "unauthorized":"Unauthorized", + "unauthorized-access":"Unauthorized Access", + "unauthorized-access-text":"You should sign in to have access to this resource!", + "access-forbidden":"Access Forbidden", + "access-forbidden-text":"You haven't access rights to this location!
Try to sign in with different user if you still wish to gain access to this location.", + "refresh-token-expired":"Session has expired", + "refresh-token-failed":"Unable to refresh session", + "permission-denied":"Permission Denied", + "permission-denied-text":"You don't have permission to perform this operation!" + }, + "account":{ + "account":"Konto", + "notification-settings":"Ustawienia powiadomień" + }, + "action":{ + "activate":"Aktywuj", + "suspend":"Zawieś", + "save":"Zapisz", + "saveAs":"Zapisz jako", + "move":"Przenieś", + "cancel":"Anuluj", + "ok":"OK", + "delete":"Usuń", + "add":"Dodaj", + "yes":"Tak", + "no":"Nie", + "update":"Aktualizuj", + "remove":"Usuń", + "select":"Wybierz", + "search":"Szukaj", + "clear-search":"Wyczyść wyszukiwanie", + "assign":"Przypisz", + "unassign":"Cofnij przypisanie", + "share":"Udostępnij", + "make-private":"Uczyń prywatnym", + "apply":"Zastosuj", + "apply-changes":"Zastosuj zmiany", + "edit-mode":"Tryb edycji", + "enter-edit-mode":"Wejdź w tryb edycji", + "decline-changes":"Odrzuć zmiany", + "decline":"Odrzuć", + "close":"Zamknij", + "back":"Wstecz", + "run":"Uruchom", + "sign-in":"Zaloguj się!", + "edit":"Edytuj", + "view":"Zobacz", + "create":"Utwórz", + "drag":"Przeciągnij", + "refresh":"Odśwież", + "undo":"Cofnij", + "copy":"Kopiuj", + "paste":"Wklej", + "copy-reference":"Kopiuj odniesienie", + "paste-reference":"Wklej odniesienie", + "import":"Importuj", + "export":"Eksportuj", + "share-via":"Udostępnij przez {{provider}}", + "continue":"Kontynuuj", + "discard-changes":"Porzuć zmiany", + "download":"Pobierz", + "next":"Dalej", + "next-with-label":"Następne: {{label}}", + "read-more":"Czytaj więcej", + "hide":"Ukryj", + "done":"Gotowe", + "print":"Drukuj", + "restore":"Przywróć", + "confirm":"Potwierdź", + "more":"Więcej", + "less":"Mniej", + "skip":"Pomiń", + "send":"Wyślij", + "reset":"Resetuj", + "show-more":"Pokaż więcej", + "dont-show-again":"Nie pokazuj ponownie", + "see-documentation":"Zobacz dokumentację", + "clear":"Wyczyść", + "upload":"Prześlij", + "delete-anyway":"Usuń mimo wszystko", + "delete-selected":"Usuń wybrane" + }, + "aggregation":{ + "aggregation":"Agregacja", + "function":"Funkcja agregacji danych", + "limit":"Maksymalna liczba wartości", + "group-interval":"Interwał grupowania", + "min":"Min", + "max":"Maks", + "avg":"Średnia", + "sum":"Suma", + "count":"Liczba", + "none":"Brak" + }, + "admin":{ + "settings":"Ustawienia", + "general":"Ogólne", + "general-settings":"Ustawienia Ogólne", + "home-settings":"Ustawienia Strony Głównej", + "home":"Strona Główna", + "outgoing-mail":"Serwer poczty", + "outgoing-mail-settings":"Ustawienia Serwera Poczty Wychodzącej", + "system-settings":"Ustawienia Systemowe", + "test-mail-sent":"Testowy email został wysłany pomyślnie!", + "base-url":"Podstawowy URL", + "base-url-required":"Podstawowy URL jest wymagany.", + "prohibit-different-url":"Zabroń używania nazwy hosta z nagłówków żądania klienta", + "prohibit-different-url-hint":"Ta opcja powinna być włączona w środowiskach produkcyjnych. Może powodować problemy bezpieczeństwa, gdy jest wyłączona", + "device-connectivity":{ + "device-connectivity":"Łączność urządzenia", + "http-s":"HTTP(s)", + "mqtt-s":"MQTT(s)", + "coap-s":"COAP(s)", + "http":"HTTP", + "https":"HTTPs", + "mqtt":"MQTT", + "mqtts":"MQTTs", + "coap":"COAP", + "coaps":"COAPs", + "hint":"Jeśli pola hosta lub portu są puste, zostanie użyta domyślna wartość protokołu.", + "host":"Host", + "port":"Port", + "port-pattern":"Port musi być dodatnią liczbą całkowitą.", + "port-range":"Port powinien mieścić się w zakresie od 1 do 65535." + }, + "mail-from":"Nadawca", + "mail-from-required":"Nadawca jest wymagany.", + "smtp-protocol":"Protokół SMTP", + "smtp-host":"Host SMTP", + "smtp-host-required":"Host SMTP jest wymagany.", + "smtp-port":"Port SMTP", + "smtp-port-required":"Musisz podać port SMTP.", + "smtp-port-invalid":"To nie wygląda na prawidłowy port SMTP.", + "timeout-msec":"Limit czasu (ms)", + "timeout-required":"Limit czasu jest wymagany.", + "timeout-invalid":"To nie wygląda na prawidłowy limit czasu.", + "enable-tls":"Włącz TLS", + "tls-version":"Wersja TLS", + "enable-proxy":"Włącz proxy", + "proxy-host":"Host proxy", + "proxy-host-required":"Host proxy jest wymagany.", + "proxy-port":"Port proxy", + "proxy-port-required":"Port proxy jest wymagany.", + "proxy-port-range":"Port proxy powinien znajdować się w zakresie od 1 do 65535.", + "proxy-user":"Użytkownik proxy", + "proxy-password":"Hasło proxy", + "change-password":"Zmień hasło", + "send-test-mail":"Wyślij mail testowy", + "sms-provider":"Dostawca SMS", + "sms-provider-settings":"Ustawienia dostawcy SMS", + "sms-provider-type":"Typ dostawcy SMS", + "sms-provider-type-required":"Typ dostawcy SMS jest wymagany.", + "sms-provider-type-aws-sns":"Amazon SNS", + "sms-provider-type-twilio":"Twilio", + "sms-provider-type-smpp":"SMPP", + "aws-access-key-id":"ID klucza dostępu AWS", + "aws-access-key-id-required":"ID klucza dostępu AWS jest wymagane", + "aws-secret-access-key":"Tajny klucz dostępu AWS", + "aws-secret-access-key-required":"Tajny klucz dostępu AWS jest wymagany", + "aws-region":"Region AWS", + "aws-region-required":"Region AWS jest wymagany", + "number-from":"Numer telefonu nadawcy", + "number-from-required":"Numer telefonu nadawcy jest wymagany.", + "number-to":"Numer telefonu odbiorcy", + "number-to-required":"Numer telefonu odbiorcy jest wymagany.", + "phone-number-hint":"Numer telefonu w formacie E.164, np. +19995550123", + "phone-number-hint-twilio":"Numer telefonu w formacie E.164/Numer SID telefonu/SID usługi wiadomości, np. +19995550123/PNXXX/MGXXX", + "phone-number-pattern":"Nieprawidłowy numer telefonu. Powinien być w formacie E.164, np. +19995550123.", + "phone-number-pattern-twilio":"Nieprawidłowy numer telefonu. Powinien być w formacie E.164/Numer SID telefonu/SID usługi wiadomości, np. +19995550123/PNXXX/MGXXX.", + "sms-message":"Wiadomość SMS", + "sms-message-required":"Wiadomość SMS jest wymagana.", + "sms-message-max-length":"Wiadomość SMS nie może być dłuższa niż 1600 znaków", + "twilio-account-sid":"SID konta Twilio", + "twilio-account-sid-required":"SID konta Twilio jest wymagany", + "twilio-account-token":"Token konta Twilio", + "twilio-account-token-required":"Token konta Twilio jest wymagany", + "send-test-sms":"Wyślij testowego SMS-a", + "test-sms-sent":"Testowy SMS został wysłany pomyślnie!", + "security-settings":"Ustawienia bezpieczeństwa", + "password-policy":"Polityka haseł", + "minimum-password-length":"Minimalna długość hasła", + "minimum-password-length-required":"Minimalna długość hasła jest wymagana", + "minimum-password-length-range":"Minimalna długość hasła powinna wynosić od 6 do 50", + "maximum-password-length":"Maksymalna długość hasła", + "maximum-password-length-min":"Maksymalna długość hasła powinna wynosić co najmniej 6", + "maximum-password-length-less-min":"Maksymalna długość hasła powinna być większa niż minimalna długość", + "minimum-uppercase-letters":"Minimalna liczba wielkich liter", + "minimum-uppercase-letters-range":"Minimalna liczba wielkich liter nie może być ujemna", + "minimum-lowercase-letters":"Minimalna liczba małych liter", + "minimum-lowercase-letters-range":"Minimalna liczba małych liter nie może być ujemna", + "minimum-digits":"Minimalna liczba cyfr", + "minimum-digits-range":"Minimalna liczba cyfr nie może być ujemna", + "minimum-special-characters":"Minimalna liczba znaków specjalnych", + "minimum-special-characters-range":"Minimalna liczba znaków specjalnych nie może być ujemna", + "password-expiration-period-days":"Okres ważności hasła w dniach", + "password-expiration-period-days-range":"Okres ważności hasła w dniach nie może być ujemny", + "password-reuse-frequency-days":"Częstotliwość ponownego używania hasła w dniach", + "password-reuse-frequency-days-range":"Częstotliwość ponownego używania hasła w dniach nie może być ujemna", + "allow-whitespace":"Zezwól na białe znaki", + "force-reset-password-if-no-valid":"Wymuś zresetowanie hasła, jeśli nie jest ważne", + "force-reset-password-if-no-valid-hint":"Ostrożnie z włączeniem tej funkcji: będzie wymagać od użytkowników z nieaktualnymi hasłami resetowania ich poprzez email.", + "general-policy":"Ogólna polityka", + "max-failed-login-attempts":"Maksymalna liczba nieudanych prób", + "minimum-max-failed-login-attempts-range":"Maksymalna liczba nieudanych prób logowania nie może być ujemna", + "user-lockout-notification-email":"W przypadku zablokowania konta użytkownika, wyślij powiadomienie na email", + "domain-name":"Nazwa domeny", + "domain-name-unique":"Nazwa domeny i protokół muszą być unikalne.", + "domain-name-max-length":"Nazwa domeny powinna być krótsza niż 256", + "error-verification-url":"Nazwa domeny nie powinna zawierać symboli '/' i ':'. Przykład: thingsboard.io", + "connection-settings":"Ustawienia połączenia", + "oauth2":{ + "access-token-uri":"URI tokenu dostępu", + "access-token-uri-required":"URI tokenu dostępu jest wymagane.", + "activate-user":"Aktywuj użytkownika", + "add-domain":"Dodaj domenę", + "delete-domain":"Usuń domenę", + "add-provider":"Dodaj dostawcę", + "delete-provider":"Usuń dostawcę", + "allow-user-creation":"Zezwalaj na tworzenie użytkowników", + "always-fullscreen":"Zawsze na pełnym ekranie", + "authorization-uri":"URI autoryzacji", + "authorization-uri-required":"URI autoryzacji jest wymagane.", + "client-authentication-method":"Metoda uwierzytelnienia klienta", + "client-id":"ID klienta", + "client-id-required":"ID klienta jest wymagane.", + "client-id-max-length":"ID klienta powinno być krótsze niż 256", + "client-secret":"Sekret klienta", + "client-secret-required":"Sekret klienta jest wymagany.", + "client-secret-max-length":"Sekret klienta powinien być krótszy niż 2049", + "custom-setting":"Niestandardowe ustawienia", + "customer-name-pattern":"Wzorzec nazwy klienta", + "customer-name-pattern-max-length":"Wzorzec nazwy klienta powinien być krótszy niż 256", + "default-dashboard-name":"Domyślna nazwa pulpitu nawigacyjnego", + "default-dashboard-name-max-length":"Domyślna nazwa pulpitu nawigacyjnego powinna być krótsza niż 256", + "delete-domain-text":"Uważaj, po potwierdzeniu domena i wszystkie dane dostawcy będą niedostępne.", + "delete-domain-title":"Czy na pewno chcesz usunąć ustawienia domeny '{{domainName}}'?", + "delete-registration-text":"Uważaj, po potwierdzeniu dane dostawcy będą niedostępne.", + "delete-registration-title":"Czy na pewno chcesz usunąć dostawcę '{{name}}'?", + "email-attribute-key":"Klucz atrybutu email", + "email-attribute-key-required":"Klucz atrybutu email jest wymagany.", + "email-attribute-key-max-length":"Klucz atrybutu email powinien być krótszy niż 32", + "first-name-attribute-key":"Klucz atrybutu imienia", + "first-name-attribute-key-max-length":"Klucz atrybutu imienia powinien być krótszy niż 32", + "general":"Ogólne", + "jwk-set-uri":"URI klucza Web JSON", + "last-name-attribute-key":"Klucz atrybutu nazwiska", + "last-name-attribute-key-max-length":"Klucz atrybutu nazwiska powinien być krótszy niż 32", + "login-button-icon":"Ikona przycisku logowania", + "login-button-label":"Etykieta dostawcy", + "login-button-label-placeholder":"Zaloguj się przez $(Etykieta dostawcy)", + "login-button-label-required":"Etykieta jest wymagana.", + "login-provider":"Dostawca logowania", + "mapper":"Mapper", + "new-domain":"Nowa domena", + "oauth2":"OAuth2", + "password-max-length":"Hasło powinno być krótsze niż 256", + "redirect-uri-template":"Szablon URI przekierowania", + "copy-redirect-uri":"Kopiuj URI przekierowania", + "registration-id":"ID rejestracji", + "registration-id-required":"ID rejestracji jest wymagane.", + "registration-id-unique":"ID rejestracji musi być unikalne dla systemu.", + "scope":"Zakres", + "scope-required":"Zakres jest wymagany.", + "tenant-name-pattern":"Wzorzec nazwy dzierżawcy", + "tenant-name-pattern-required":"Wzorzec nazwy dzierżawcy jest wymagany.", + "tenant-name-pattern-max-length":"Wzorzec nazwy dzierżawcy powinien być krótszy niż 256", + "tenant-name-strategy":"Strategia nazewnictwa dzierżawcy", + "type":"Typ mappera", + "uri-pattern-error":"Nieprawidłowy format URI.", + "url":"URL", + "url-pattern":"Nieprawidłowy format URL.", + "url-required":"URL jest wymagany.", + "url-max-length":"URL powinien być krótszy niż 256", + "user-info-uri":"URI informacji o użytkowniku", + "user-info-uri-required":"URI informacji o użytkowniku jest wymagane.", + "username-max-length":"Nazwa użytkownika powinna być krótsza niż 256", + "user-name-attribute-name":"Klucz atrybutu nazwy użytkownika", + "user-name-attribute-name-required":"Klucz atrybutu nazwy użytkownika jest wymagany", + "protocol":"Protokół", + "domain-schema-http":"HTTP", + "domain-schema-https":"HTTPS", + "domain-schema-mixed":"HTTP+HTTPS", + "enable":"Włącz ustawienia OAuth2", + "domains":"Domeny", + "mobile-apps":"Aplikacje mobilne", + "no-mobile-apps":"Brak skonfigurowanych aplikacji", + "mobile-package":"Pakiet aplikacji", + "mobile-package-placeholder":"Np.: my.example.app", + "mobile-package-hint":"Dla Androida: unikalny ID aplikacji. Dla iOS: identyfikator paczki produktu.", + "mobile-package-unique":"Pakiet aplikacji musi być unikalny.", + "mobile-app-secret":"Sekret aplikacji", + "invalid-mobile-app-secret":"Sekret aplikacji musi zawierać tylko znaki alfanumeryczne i mieć od 16 do 2048 znaków.", + "copy-mobile-app-secret":"Kopiuj sekret aplikacji", + "add-mobile-app":"Dodaj aplikację", + "delete-mobile-app":"Usuń informacje o aplikacji", + "providers":"Dostawcy", + "platform-web":"Web", + "platform-android":"Android", + "platform-ios":"iOS", + "all-platforms":"Wszystkie platformy", + "smtp-provider":"Dostawca SMTP", + "allowed-platforms":"Dozwolone platformy", + "authentication":"Autentykacja", + "basic":"Podstawowy", + "provider":"Dostawca", + "redirect-url":"URI przekierowania", + "domain-name":"Nazwa domeny", + "redirect-url-template":"Szablon URI przekierowania", + "microsoft-tenant-id":"ID dzierżawy (tenant) Microsoft", + "microsoft-tenant-id-required":"ID dzierżawy (tenant) Microsoft jest wymagane", + "token-uri":"URI tokenu", + "token-uri-required":"URI tokenu jest wymagane", + "redirect-uri":"URI przekierowania", + "google-provider":"Google", + "microsoft-provider":"Office 365", + "sendgrid-provider":"Sendgrid", + "custom-provider":"Niestandardowy", + "generate-access-token":"Generuj token dostępu", + "update-access-token":"Aktualizuj token dostępu", + "access-token-status":"Status tokenu dostępu:", + "token-status-generated":"wygenerowany", + "token-status-not-generated":"nie wygenerowany" + }, + "smpp-provider":{ + "smpp-version":"Wersja SMPP", + "smpp-host":"Host SMPP", + "smpp-host-required":"Host SMPP jest wymagany", + "smpp-port":"Port SMPP", + "smpp-port-required":"Port SMPP jest wymagany", + "system-id":"ID systemu", + "system-id-required":"ID systemu jest wymagane", + "password":"Hasło", + "password-required":"Hasło jest wymagane", + "type-settings":"Ustawienia typu", + "source-settings":"Ustawienia źródła", + "destination-settings":"Ustawienia docelowe", + "additional-settings":"Dodatkowe ustawienia", + "system-type":"Typ systemu", + "bind-type":"Typ powiązania", + "service-type":"Typ usługi", + "source-address":"Adres źródłowy", + "source-ton":"TON źródłowy", + "source-npi":"NPI źródłowy", + "destination-ton":"TON docelowy (Typ Numeru)", + "destination-npi":"NPI docelowy (Identyfikacja Planu Numeracji)", + "address-range":"Zakres adresów", + "coding-scheme":"Schemat kodowania", + "bind-type-tx":"Nadajnik", + "bind-type-rx":"Odbiornik", + "bind-type-trx":"Nadawczo-odbiorczy", + "ton-unknown":"Nieznany", + "ton-international":"Międzynarodowy", + "ton-national":"Krajowy", + "ton-network-specific":"Specyficzny dla sieci", + "ton-subscriber-number":"Numer abonenta", + "ton-alphanumeric":"Alfanumeryczny", + "ton-abbreviated":"Skrócony", + "npi-unknown":"0 - Nieznany", + "npi-isdn":"1 - ISDN/plan numeracji telefonicznej (E163/E164)", + "npi-data-numbering-plan":"3 - Plan numeracji danych (X.121)", + "npi-telex-numbering-plan":"4 - Plan numeracji telexu (F.69)", + "npi-land-mobile":"6 - Mobilny lądowy (E.212)", + "npi-national-numbering-plan":"8 - Krajowy plan numeracji", + "npi-private-numbering-plan":"9 - Prywatny plan numeracji", + "npi-ermes-numbering-plan":"10 - Plan numeracji ERMES (ETSI DE/PS 3 01-3)", + "npi-internet":"13 - Internet (IP)", + "npi-wap-client-id":"18 - Identyfikator klienta WAP (do zdefiniowania przez Forum WAP)", + "scheme-smsc":"0 - Domyślny alfabet SMSC (ASCII dla krótkiego i długiego kodu oraz do GSM dla bezpłatnych)", + "scheme-ia5":"1 - IA5 (ASCII dla krótkiego i długiego kodu, łaciński 9 dla bezpłatnych (ISO-8859-9))", + "scheme-octet-unspecified-2":"2 - Oktet nieokreślony (binarny 8-bitowy)", + "scheme-latin-1":"3 - Łaciński 1 (ISO-8859-1)", + "scheme-octet-unspecified-4":"4 - Oktet nieokreślony (binarny 8-bitowy)", + "scheme-jis":"5 - JIS (X 0208-1990)", + "scheme-cyrillic":"6 - Cyrylica (ISO-8859-5)", + "scheme-latin-hebrew":"7 - Łaciński/hebrajski (ISO-8859-8)", + "scheme-ucs-utf":"8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding":"9 - Kodowanie piktogramów", + "scheme-music-codes":"10 - Kody muzyczne (ISO-2022-JP)", + "scheme-extended-kanji-jis":"13 - Rozszerzony Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set":"14 - Zestaw koreańskich znaków graficznych (KS C 5601/KS X 1001)" + }, + "queue-select-name":"Wybierz nazwę kolejki", + "queue-name":"Nazwa", + "queue-name-required":"Nazwa kolejki jest wymagana!", + "queues":"Kolejki", + "queue-partitions":"Partycje", + "queue-submit-strategy":"Strategia zgłaszania", + "queue-processing-strategy":"Strategia przetwarzania", + "queue-configuration":"Konfiguracja kolejki", + "repository-settings":"Ustawienia repozytorium", + "repository":"Repozytorium", + "repository-url":"URL repozytorium", + "repository-url-required":"URL repozytorium jest wymagany.", + "default-branch":"Domyślna nazwa gałęzi", + "repository-read-only":"Tylko do odczytu", + "show-merge-commits":"Pokaż commity scalające", + "authentication-settings":"Ustawienia autentykacji", + "auth-method":"Metoda uwierzytelniania", + "auth-method-username-password":"Hasło / token dostępu", + "auth-method-username-password-hint":"Użytkownicy GitHub muszą używać tokenów dostępu z uprawnieniami do zapisu w repozytorium.", + "auth-method-private-key":"Klucz prywatny", + "password-access-token":"Hasło / token dostępu", + "change-password-access-token":"Zmień hasło / token dostępu", + "private-key":"Klucz prywatny", + "drop-private-key-file-or":"Przeciągnij i upuść plik klucza prywatnego lub", + "passphrase":"Hasło", + "enter-passphrase":"Wprowadź hasło", + "change-passphrase":"Zmień hasło", + "check-access":"Sprawdź dostęp", + "check-repository-access-success":"Dostęp do repozytorium pomyślnie zweryfikowany!", + "delete-repository-settings-title":"Czy na pewno chcesz usunąć ustawienia repozytorium?", + "delete-repository-settings-text":"Uważaj, po potwierdzeniu ustawienia repozytorium zostaną usunięte, a funkcja kontroli wersji będzie niedostępna.", + "auto-commit-settings":"Ustawienia automatycznego zatwierdzania", + "auto-commit":"Automatyczne zatwierdzanie", + "auto-commit-entities":"Automatyczne zatwierdzanie encji", + "no-auto-commit-entities-prompt":"Brak skonfigurowanych encji do automatycznego zatwierdzania", + "delete-auto-commit-settings-title":"Czy na pewno chcesz usunąć ustawienia automatycznego zatwierdzania?", + "delete-auto-commit-settings-text":"Uważaj, po potwierdzeniu ustawienia automatycznego zatwierdzania zostaną usunięte, a automatyczne zatwierdzanie zostanie wyłączone dla wszystkich encji.", + "2fa":{ + "2fa":"Dwuetapowa autentykacja", + "available-providers":"Dostępni dostawcy", + "issuer-name":"Nazwa wydawcy", + "issuer-name-required":"Nazwa wydawcy jest wymagana.", + "max-verification-failures-before-user-lockout":"Maksymalna liczba nieudanych weryfikacji przed zablokowaniem użytkownika", + "max-verification-failures-before-user-lockout-pattern":"Maksymalna liczba nieudanych weryfikacji musi być dodatnią liczbą całkowitą.", + "number-of-checking-attempts":"Liczba prób sprawdzenia", + "number-of-checking-attempts-pattern":"Liczba prób sprawdzenia musi być dodatnią liczbą całkowitą.", + "number-of-checking-attempts-required":"Liczba prób sprawdzenia jest wymagana.", + "number-of-codes":"Liczba kodów", + "number-of-codes-pattern":"Liczba kodów musi być dodatnią liczbą całkowitą.", + "number-of-codes-required":"Liczba kodów jest wymagana.", + "provider":"Dostawca", + "retry-verification-code-period":"Okres ponownego próbowania kodu weryfikacyjnego (sek)", + "retry-verification-code-period-pattern":"Minimalny czas okresu to 5 sek", + "retry-verification-code-period-required":"Okres ponownego próbowania kodu weryfikacyjnego jest wymagany.", + "total-allowed-time-for-verification":"Całkowity dozwolony czas na weryfikację (sek)", + "total-allowed-time-for-verification-pattern":"Minimalny całkowity dozwolony czas to 60 sek", + "total-allowed-time-for-verification-required":"Całkowity dozwolony czas jest wymagany.", + "use-system-two-factor-auth-settings":"Użyj systemowych ustawień dwuetapowej autentykacji", + "verification-code-check-rate-limit":"Limit częstotliwości sprawdzania kodu weryfikacyjnego", + "verification-code-lifetime":"Czas życia kodu weryfikacyjnego (sek)", + "verification-code-lifetime-pattern":"Czas życia kodu weryfikacyjnego musi być dodatnią liczbą całkowitą.", + "verification-code-lifetime-required":"Czas życia kodu weryfikacyjnego jest wymagany.", + "verification-message-template":"Szablon wiadomości weryfikacyjnej", + "verification-limitations":"Ograniczenia weryfikacji", + "verification-message-template-pattern":"Wiadomość weryfikacyjna musi zawierać wzór: ${code}", + "verification-message-template-required":"Szablon wiadomości weryfikacyjnej jest wymagany.", + "within-time":"W czasie (sek)", + "within-time-pattern":"Czas musi być dodatnią liczbą całkowitą.", + "within-time-required":"Czas jest wymagany." + }, + "jwt":{ + "security-settings":"Ustawienia bezpieczeństwa JWT", + "issuer-name":"Nazwa wydawcy", + "issuer-name-required":"Nazwa wydawcy jest wymagana.", + "signings-key":"Klucz podpisujący", + "signings-key-hint":"Kodowany Base64 ciąg reprezentujący co najmniej 256 bitów danych.", + "signings-key-required":"Klucz podpisujący jest wymagany.", + "signings-key-min-length":"Klucz podpisujący musi reprezentować co najmniej 256 bitów danych.", + "signings-key-base64":"Klucz podpisujący musi być w formacie base64.", + "expiration-time":"Czas wygaśnięcia tokena (sek)", + "expiration-time-required":"Czas wygaśnięcia tokena jest wymagany.", + "expiration-time-pattern":"Czas wygaśnięcia tokena musi być dodatnią liczbą całkowitą.", + "expiration-time-min":"Minimalny czas to 60 sekund (1 minuta).", + "refresh-expiration-time":"Czas wygaśnięcia tokena odświeżającego (sek)", + "refresh-expiration-time-required":"Czas wygaśnięcia tokena odświeżającego jest wymagany.", + "refresh-expiration-time-pattern":"Czas wygaśnięcia tokena odświeżającego musi być dodatnią liczbą całkowitą.", + "refresh-expiration-time-min":"Minimalny czas to 900 sekund (15 minut).", + "refresh-expiration-time-less-token":"Czas tokena odświeżającego musi być dłuższy niż czas tokena.", + "generate-key":"Wygeneruj klucz", + "info-header":"Wszyscy użytkownicy będą musieli ponownie się zalogować", + "info-message":"Zmiana klucza podpisującego JWT spowoduje unieważnienie wszystkich wydanych tokenów. Wszyscy użytkownicy będą musieli ponownie się zalogować. Dotyczy to również skryptów korzystających z Rest API/Websockets." + }, + "resources":"Zasoby", + "notifications":"Powiadomienia", + "notifications-settings":"Ustawienia powiadomień", + "slack-api-token":"Token API Slack", + "slack":"Slack", + "slack-settings":"Ustawienia Slack" + }, + "alarm":{ + "alarm":"Alarm", + "alarms":"Alarmy", + "all-alarms":"Wszystkie alarmy", + "select-alarm":"Wybierz alarm", + "no-alarms-matching":"Nie znaleziono alarmów pasujących do '{{entity}}'.", + "alarm-required":"Alarm jest wymagany", + "alarm-filter":"Filtr alarmów", + "filter":"Filtr", + "alarm-status":"Status alarmu", + "alarm-status-list":"Lista statusów alarmów", + "any-status":"Dowolny status", + "search-status":{ + "ANY":"Dowolny", + "ACTIVE":"Aktywny", + "CLEARED":"Wyczyszczony", + "ACK":"Potwierdzony", + "UNACK":"Niepotwierdzony" + }, + "display-status":{ + "ACTIVE_UNACK":"Aktywny Niepotwierdzony", + "ACTIVE_ACK":"Aktywny Potwierdzony", + "CLEARED_UNACK":"Wyczyszczony Niepotwierdzony", + "CLEARED_ACK":"Wyczyszczony Potwierdzony" + }, + "no-alarms-prompt":"Nie znaleziono alarmów", + "created-time":"Czas utworzenia", + "type":"Typ", + "severity":"Waga", + "originator":"Inicjator", + "originator-type":"Typ inicjatora", + "details":"Szczegóły", + "originator-label":"Etykieta inicjatora", + "assign":"Przypisz", + "assignments":"Przypisania", + "assignee":"Przypisany", + "assignee-id":"ID przypisanego", + "assignee-first-name":"Imię przypisanego", + "assignee-last-name":"Nazwisko przypisanego", + "assignee-email":"Email przypisanego", + "unassigned":"Nieprzypisany", + "assignee-not-set":"Wszystkie", + "status":"Status", + "alarm-details":"Szczegóły alarmu", + "start-time":"Czas rozpoczęcia", + "assign-time":"Czas przypisania", + "end-time":"Czas zakończenia", + "ack-time":"Czas potwierdzenia", + "clear-time":"Czas wyczyszczenia", + "duration":"Czas trwania", + "alarm-severity-list":"Lista ważności alarmów", + "any-severity":"Dowolna ważność", + "severity-critical":"Krytyczna", + "severity-major":"Poważna", + "severity-minor":"Mniejsza", + "severity-warning":"Ostrzeżenie", + "severity-indeterminate":"Nieokreślona", + "acknowledge":"Potwierdź", + "clear":"Wyczyść", + "delete":"Usuń", + "search":"Szukaj alarmów", + "selected-alarms":"{ count, plural, =1 {1 alarm} other {# alarmów} } wybrano", + "no-data":"Brak danych do wyświetlenia", + "polling-interval":"Interwał odpytywania alarmów (sek)", + "polling-interval-required":"Interwał odpytywania alarmów jest wymagany.", + "min-polling-interval-message":"Dozwolony jest minimalny interwał odpytywania 1 sek.", + "aknowledge-alarms-title":"Potwierdź { count, plural, =1 {1 alarm} other {# alarmów} }", + "aknowledge-alarms-text":"Czy na pewno chcesz potwierdzić { count, plural, =1 {1 alarm} other {# alarmów} }?", + "aknowledge-alarm-title":"Potwierdź alarm", + "aknowledge-alarm-text":"Czy na pewno chcesz potwierdzić alarm?", + "selected-alarms-are-acknowledged":"Wybrane alarmy zostały już potwierdzone", + "clear-alarms-title":"Wyczyść { count, plural, =1 {1 alarm} other {# alarmów} }", + "clear-alarms-text":"Czy na pewno chcesz wyczyścić { count, plural, =1 {1 alarm} other {# alarmów} }?", + "clear-alarm-title":"Wyczyść alarm", + "clear-alarm-text":"Czy na pewno chcesz wyczyścić alarm?", + "delete-alarms-title":"Usuń { count, plural, =1 {1 alarm} other {# alarmów} }", + "delete-alarms-text":"Czy na pewno chcesz usunąć { count, plural, =1 {1 alarm} other {# alarmów} }?", + "selected-alarms-are-cleared":"Wybrane alarmy zostały już wyczyszczone", + "alarm-status-filter":"Filtr statusu alarmów", + "alarm-filter-title":"Filtr alarmów", + "assigned":"Przypisane", + "filter-title":"Filtr", + "max-count-load":"Maksymalna liczba alarmów do załadowania (0 - bez limitu)", + "max-count-load-required":"Maksymalna liczba alarmów do załadowania jest wymagana.", + "max-count-load-error-min":"Minimalna wartość to 0.", + "fetch-size":"Rozmiar pobrania", + "fetch-size-required":"Rozmiar pobrania jest wymagany.", + "fetch-size-error-min":"Minimalna wartość to 10.", + "alarm-type-list":"Lista typów alarmów", + "any-type":"Dowolny typ", + "assigned-to-current-user":"Przypisane do aktualnego użytkownika", + "assigned-to-me":"Przypisane do mnie", + "search-propagated-alarms":"Szukaj propagowanych alarmów", + "comments":"Komentarze do alarmu", + "show-more":"Pokaż więcej", + "additional-info":"Dodatkowe informacje", + "alarm-type":"Typ alarmu", + "enter-alarm-type":"Wpisz typ alarmu", + "no-alarm-types-matching":"Nie znaleziono typów alarmów pasujących do '{{entitySubtype}}'.", + "alarm-type-list-empty":"Nie wybrano typów alarmów." + }, + "alarm-activity":{ + "add":"Dodaj komentarz...", + "alarm-comment":"Komentarz do alarmu", + "comments":"Komentarze", + "delete-alarm-comment":"Czy chcesz usunąć ten komentarz?", + "refresh":"Odśwież", + "oldest-first":"Najstarsze najpierw", + "newest-first":"Najnowsze najpierw", + "activity":"Aktywność", + "export":"Eksportuj do CSV", + "author":"Autor", + "created-date":"Data utworzenia", + "edited-date":"Data edycji", + "text":"Tekst", + "system":"System" + }, + "alias":{ + "add":"Dodaj alias", + "edit":"Edytuj alias", + "name":"Nazwa aliasu", + "name-required":"Nazwa aliasu jest wymagana", + "duplicate-alias":"Alias o tej samej nazwie już istnieje.", + "filter-type-single-entity":"Pojedyncza encja", + "filter-type-entity-list":"Lista encji", + "filter-type-entity-name":"Nazwa encji", + "filter-type-entity-type":"Typ encji", + "filter-type-state-entity":"Encja ze stanu pulpitu nawigacyjnego", + "filter-type-state-entity-description":"Encja pobrana z parametrów stanu pulpitu nawigacyjnego", + "filter-type-asset-type":"Typ aktywa", + "filter-type-asset-type-description":"Aktywa typu '{{assetTypes}}'", + "filter-type-asset-type-and-name-description":"Aktywa typu '{{assetTypes}}' o nazwie rozpoczynającej się od '{{prefix}}'", + "filter-type-device-type":"Typ urządzenia", + "filter-type-device-type-description":"Urządzenia typu '{{deviceTypes}}'", + "filter-type-device-type-and-name-description":"Urządzenia typu '{{deviceTypes}}' o nazwie rozpoczynającej się od '{{prefix}}'", + "filter-type-entity-view-type":"Typ widoku encji", + "filter-type-entity-view-type-description":"Widoki encji typu '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description":"Widoki encji typu '{{entityViewTypes}}' o nazwie rozpoczynającej się od '{{prefix}}'", + "filter-type-edge-type":"Typ krawędzi", + "filter-type-edge-type-description":"Krawędzie typu '{{edgeTypes}}'", + "filter-type-edge-type-and-name-description":"Krawędzie typu '{{edgeTypes}}' o nazwie rozpoczynającej się od '{{prefix}}'", + "filter-type-relations-query":"Zapytanie o relacje", + "filter-type-relations-query-description":"{{entities}} mające relację typu {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-asset-search-query":"Zapytanie wyszukiwania aktywów", + "filter-type-asset-search-query-description":"Aktywa typu {{assetTypes}} mające relację typu {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-device-search-query":"Zapytanie wyszukiwania urządzeń", + "filter-type-device-search-query-description":"Urządzenia typu {{deviceTypes}} mające relację typu {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query":"Zapytanie wyszukiwania widoków encji", + "filter-type-entity-view-search-query-description":"Widoki encji typu {{entityViewTypes}} mające relację typu {{relationType}} {{direction}} {{rootEntity}}", + "filter-type-apiUsageState":"Stan użycia API", + "filter-type-edge-search-query":"Zapytanie wyszukiwania krawędzi", + "filter-type-edge-search-query-description":"Krawędzie typu {{edgeTypes}} mające relację typu {{relationType}} {{direction}} {{rootEntity}}", + "entity-filter":"Filtr encji", + "resolve-multiple":"Rozwiąż jako wiele encji", + "filter-type":"Typ filtra", + "filter-type-required":"Typ filtra jest wymagany.", + "entity-filter-no-entity-matched":"Nie znaleziono encji pasujących do określonego filtra.", + "no-entity-filter-specified":"Nie określono filtra encji", + "root-state-entity":"Użyj encji stanu pulpitu nawigacyjnego jako korzenia", + "last-level-relation":"Pobierz tylko relacje ostatniego poziomu", + "root-entity":"Encja główna", + "state-entity-parameter-name":"Nazwa parametru encji stanu", + "default-state-entity":"Domyślna encja stanu", + "default-entity-parameter-name":"Domyślnie", + "max-relation-level":"Maksymalny poziom relacji", + "unlimited-level":"Nieograniczony poziom", + "state-entity":"Encja stanu pulpitu nawigacyjnego", + "all-entities":"Wszystkie encje", + "any-relation":"dowolna" + }, + "asset":{ + "asset":"Aktywa", + "assets":"Aktywa", + "management":"Zarządzanie aktywami", + "view-assets":"Wyświetl aktywa", + "add":"Dodaj aktywa", + "asset-type-max-length":"Typ aktywa powinien być krótszy niż 256", + "assign-to-customer":"Przypisz do klienta", + "assign-asset-to-customer":"Przypisz aktywa do klienta", + "assign-asset-to-customer-text":"Proszę wybrać aktywa do przypisania klientowi", + "no-assets-text":"Nie znaleziono aktywów", + "assign-to-customer-text":"Proszę wybrać klienta do przypisania aktywów", + "public":"Publiczne", + "assignedToCustomer":"Przypisane do klienta", + "make-public":"Upublicznij aktywa", + "make-private":"Prywatyzuj aktywa", + "unassign-from-customer":"Usuń przypisanie od klienta", + "delete":"Usuń aktywa", + "asset-public":"Aktywa są publiczne", + "asset-type":"Typ aktywa", + "asset-type-required":"Typ aktywa jest wymagany.", + "select-asset-type":"Wybierz typ aktywa", + "enter-asset-type":"Wprowadź typ profilu aktywa", + "any-asset":"Dowolne aktywa", + "no-asset-types-matching":"Nie znaleziono typów aktywów pasujących do '{{entitySubtype}}'.", + "asset-type-list-empty":"Nie wybrano typów aktywów.", + "asset-types":"Typy aktywów", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "name-max-length":"Nazwa powinna być krótsza niż 256", + "label-max-length":"Etykieta powinna być krótsza niż 256", + "description":"Opis", + "type":"Typ", + "type-required":"Typ jest wymagany.", + "details":"Szczegóły", + "events":"Wydarzenia", + "add-asset-text":"Dodaj nowe aktywa", + "asset-details":"Szczegóły aktywa", + "assign-assets":"Przypisz aktywa", + "assign-assets-text":"Przypisz { count, plural, =1 {1 aktywa} other {# aktywów} } do klienta", + "assign-asset-to-edge-title":"Przypisz aktywa do krawędzi", + "assign-asset-to-edge-text":"Proszę wybrać aktywa do przypisania do krawędzi", + "delete-assets":"Usuń aktywa", + "unassign-assets":"Usuń przypisanie aktywów", + "unassign-assets-action-title":"Usuń przypisanie { count, plural, =1 {1 aktywa} other {# aktywów} } od klienta", + "assign-new-asset":"Przypisz nowe aktywa", + "delete-asset-title":"Czy na pewno chcesz usunąć aktywa '{{assetName}}'?", + "delete-asset-text":"Ostrożnie, po potwierdzeniu aktywa oraz wszystkie powiązane dane staną się nieodwracalne.", + "delete-assets-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 aktywa} other {# aktywów} }?", + "delete-assets-action-title":"Usuń { count, plural, =1 {1 aktywa} other {# aktywów} }", + "delete-assets-text":"Ostrożnie, po potwierdzeniu wszystkie wybrane aktywa oraz wszystkie powiązane dane staną się nieodwracalne.", + "make-public-asset-title":"Czy na pewno chcesz uczynić aktywa '{{assetName}}' publicznymi?", + "make-public-asset-text":"Po potwierdzeniu aktywa oraz wszystkie jego dane staną się publicznie dostępne.", + "make-private-asset-title":"Czy na pewno chcesz uczynić aktywa '{{assetName}}' prywatnymi?", + "make-private-asset-text":"Po potwierdzeniu aktywa oraz wszystkie jego dane staną się prywatne i niedostępne dla innych.", + "unassign-asset-title":"Czy na pewno chcesz usunąć przypisanie aktywów '{{assetName}}'?", + "unassign-asset-text":"Po potwierdzeniu aktywa zostaną usunięte z przypisania i nie będą dostępne dla klienta.", + "unassign-asset":"Usuń przypisanie aktywów", + "unassign-assets-title":"Czy na pewno chcesz usunąć przypisanie { count, plural, =1 {1 aktywa} other {# aktywów} }?", + "unassign-assets-text":"Po potwierdzeniu wszystkie wybrane aktywa zostaną usunięte z przypisania i nie będą dostępne dla klienta.", + "unassign-assets-from-edge":"Usuń przypisanie aktywów od krawędzi", + "copyId":"Kopiuj ID aktywów", + "idCopiedMessage":"ID aktywów zostało skopiowane do schowka", + "select-asset":"Wybierz aktywa", + "no-assets-matching":"Nie znaleziono aktywów pasujących do '{{entity}}'.", + "asset-required":"Aktywa są wymagane", + "name-starts-with":"Wyrażenie nazwy aktywów", + "help-text":"Użyj '%', zgodnie z potrzebą: '%nazwa_zawiera_aktywa%', '%nazwa_kończy_się_aktywami', 'nazwa_rozpoczyna_się_aktywami'.", + "import":"Importuj aktywa", + "asset-file":"Plik aktywów", + "label":"Etykieta", + "search":"Wyszukaj aktywa", + "assign-asset-to-edge":"Przypisz aktywa do krawędzi", + "unassign-asset-from-edge":"Usuń przypisanie aktywów od krawędzi", + "unassign-asset-from-edge-title":"Czy na pewno chcesz usunąć przypisanie aktywów '{{assetName}}' od krawędzi?", + "unassign-asset-from-edge-text":"Po potwierdzeniu aktywa zostaną usunięte z przypisania od krawędzi i nie będą dostępne dla tej krawędzi.", + "unassign-assets-from-edge-title":"Czy na pewno chcesz usunąć przypisanie { count, plural, =1 {1 aktywa} other {# aktywów} } od krawędzi?", + "unassign-assets-from-edge-text":"Po potwierdzeniu wszystkie wybrane aktywa zostaną usunięte z przypisania od krawędzi i nie będą dostępne dla tej krawędzi.", + "selected-assets":"{ count, plural, =1 {1 aktywo} other {# aktywów} } wybrane" + }, + "attribute":{ + "attributes":"Atrybuty", + "latest-telemetry":"Najnowsza telemetria", + "no-latest-telemetry":"Brak najnowszej telemetrii", + "attributes-scope":"Zakres atrybutów encji", + "scope-telemetry":"Telemetria", + "scope-latest-telemetry":"Najnowsza telemetria", + "scope-client":"Atrybuty klienta", + "scope-server":"Atrybuty serwera", + "scope-shared":"Atrybuty współdzielone", + "add":"Dodaj atrybut", + "key":"Klucz", + "key-max-length":"Klucz powinien być krótszy niż 256", + "last-update-time":"Czas ostatniej aktualizacji", + "key-required":"Klucz atrybutu jest wymagany.", + "value":"Wartość", + "value-required":"Wartość atrybutu jest wymagana.", + "telemetry-key-required":"Klucz telemetrii jest wymagany", + "telemetry-value-required":"Wartość telemetrii jest wymagana", + "delete-attributes-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 atrybut} other {# atrybutów} }?", + "delete-attributes-text":"Ostrożnie, po potwierdzeniu wszystkie wybrane atrybuty zostaną usunięte.", + "delete-attributes":"Usuń atrybuty", + "enter-attribute-value":"Wprowadź wartość atrybutu", + "show-on-widget":"Pokaż na widżecie", + "widget-mode":"Tryb widżetu", + "next-widget":"Następny widżet", + "prev-widget":"Poprzedni widżet", + "add-to-dashboard":"Dodaj do pulpitu nawigacyjnego", + "add-widget-to-dashboard":"Dodaj widżet do pulpitu nawigacyjnego", + "selected-attributes":"{ count, plural, =1 {1 atrybut} other {# atrybutów} } wybrano", + "selected-telemetry":"{ count, plural, =1 {1 jednostka telemetrii} other {# jednostek telemetrii} } wybrano", + "no-attributes-text":"Nie znaleziono atrybutów", + "no-telemetry-text":"Nie znaleziono telemetrii", + "copy-key":"Kopiuj klucz", + "add-telemetry":"Dodaj telemetrię", + "copy-value":"Kopiuj wartość", + "delete-timeseries":{ + "start-time":"Czas rozpoczęcia", + "ends-on":"Kończy się", + "strategy":"Strategia", + "delete-strategy":"Strategia usuwania", + "all-data":"Usuń wszystkie dane", + "all-data-except-latest-value":"Usuń wszystkie dane oprócz najnowszej wartości", + "latest-value":"Usuń najnowszą wartość", + "all-data-for-time-period":"Usuń wszystkie dane za okres czasu", + "rewrite-latest-value":"Zastąp najnowszą wartość" + } + }, + "api-usage":{ + "api-features":"Funkcje API", + "api-usage":"Użycie API", + "alarm":"Alarm", + "alarms-created":"Utworzone alarmy", + "alarms-created-daily-activity":"Dzienna aktywność utworzonych alarmów", + "alarms-created-hourly-activity":"Godzinna aktywność utworzonych alarmów", + "alarms-created-monthly-activity":"Miesięczna aktywność utworzonych alarmów", + "data-points":"Punkty danych", + "data-points-storage-days":"Dni przechowywania punktów danych", + "device-api":"API urządzenia", + "email":"Email", + "email-messages":"Wiadomości e-mail", + "email-messages-daily-activity":"Dzienna aktywność wiadomości e-mail", + "email-messages-monthly-activity":"Miesięczna aktywność wiadomości e-mail", + "exceptions":"Wyjątki", + "executions":"Wykonania", + "scripts":"Skrypty", + "scripts-hourly-activity":"Godzinna aktywność skryptów", + "scripts-daily-activity":"Dzienna aktywność skryptów", + "scripts-monthly-activity":"Miesięczna aktywność skryptów", + "javascript":"JavaScript", + "javascript-executions":"Wykonania JavaScript", + "tbel":"TBEL", + "tbel-executions":"Wykonania TBEL", + "latest-error":"Ostatni błąd", + "messages":"Wiadomości", + "notifications":"Powiadomienia", + "notifications-email-sms":"Powiadomienia (Email/SMS)", + "notifications-hourly-activity":"Godzinna aktywność powiadomień", + "permanent-failures":"Stałe awarie ${entityName}", + "permanent-timeouts":"Stałe przekroczenia czasu ${entityName}", + "processing-failures":"Błędy przetwarzania ${entityName}", + "processing-failures-and-timeouts":"Błędy przetwarzania i przekroczenia czasu", + "processing-timeouts":"Przekroczenia czasu przetwarzania ${entityName}", + "queue-stats":"Statystyki kolejki", + "rule-chain":"Łańcuch reguł", + "rule-engine":"Silnik reguł", + "rule-engine-daily-activity":"Dzienna aktywność silnika reguł", + "rule-engine-executions":"Wykonania silnika reguł", + "rule-engine-hourly-activity":"Godzinna aktywność silnika reguł", + "rule-engine-monthly-activity":"Miesięczna aktywność silnika reguł", + "rule-engine-statistics":"Statystyki silnika reguł", + "rule-node":"Węzeł reguł", + "sms":"SMS", + "sms-messages":"Wiadomości SMS", + "sms-messages-daily-activity":"Dzienna aktywność wiadomości SMS", + "sms-messages-monthly-activity":"Miesięczna aktywność wiadomości SMS", + "successful":"Udane ${entityName}", + "telemetry":"Telemetria", + "telemetry-persistence":"Przechowywanie telemetrii", + "telemetry-persistence-daily-activity":"Dzienna aktywność przechowywania telemetrii", + "telemetry-persistence-hourly-activity":"Godzinna aktywność przechowywania telemetrii", + "telemetry-persistence-monthly-activity":"Miesięczna aktywność przechowywania telemetrii", + "transport":"Transport", + "transport-daily-activity":"Dzienna aktywność transportu", + "transport-data-points":"Punkty danych transportu", + "transport-hourly-activity":"Godzinna aktywność transportu", + "transport-messages":"Wiadomości transportu", + "transport-monthly-activity":"Miesięczna aktywność transportu", + "view-details":"Wyświetl szczegóły", + "view-statistics":"Wyświetl statystyki" + }, + "api-limit":{ + "cassandra-queries":"Zapytania do Cassandry", + "entity-version-creation":"Tworzenie wersji encji", + "entity-version-load":"Ładowanie wersji encji", + "notification-requests":"Żądania powiadomień", + "notification-requests-per-rule":"Żądania powiadomień na regułę", + "rest-api-requests":"Żądania REST API", + "rest-api-requests-per-customer":"Żądania REST API na klienta", + "transport-messages":"Wiadomości transportowe", + "transport-messages-per-device":"Wiadomości transportowe na urządzenie", + "ws-updates-per-session":"Aktualizacje WS na sesję" + }, + "audit-log":{ + "audit":"Audyt", + "audit-logs":"Dzienniki audytu", + "timestamp":"Znacznik czasu", + "entity-type":"Typ encji", + "entity-name":"Nazwa encji", + "user":"Użytkownik", + "type":"Typ", + "status":"Status", + "details":"Szczegóły", + "type-added":"Dodano", + "type-deleted":"Usunięto", + "type-updated":"Zaktualizowano", + "type-attributes-updated":"Zaktualizowano atrybuty", + "type-attributes-deleted":"Usunięto atrybuty", + "type-rpc-call":"Wywołanie RPC", + "type-credentials-updated":"Zaktualizowano dane uwierzytelniające", + "type-assigned-to-customer":"Przypisano do klienta", + "type-unassigned-from-customer":"Usunięto przypisanie od klienta", + "type-assigned-to-edge":"Przypisano do krawędzi", + "type-unassigned-from-edge":"Usunięto przypisanie od krawędzi", + "type-activated":"Aktywowano", + "type-suspended":"Zawieszono", + "type-credentials-read":"Odczytano dane uwierzytelniające", + "type-attributes-read":"Odczytano atrybuty", + "type-relation-add-or-update":"Zaktualizowano relację", + "type-relation-delete":"Usunięto relację", + "type-relations-delete":"Usunięto wszystkie relacje", + "type-alarm-ack":"Potwierdzono", + "type-alarm-clear":"Wyczyszczono", + "type-alarm-assign":"Przypisano", + "type-alarm-unassign":"Usunięto przypisanie", + "type-added-comment":"Dodano komentarz", + "type-updated-comment":"Zaktualizowano komentarz", + "type-deleted-comment":"Usunięto komentarz", + "type-login":"Logowanie", + "type-logout":"Wylogowanie", + "type-lockout":"Blokada", + "status-success":"Sukces", + "status-failure":"Niepowodzenie", + "audit-log-details":"Szczegóły dziennika audytu", + "no-audit-logs-prompt":"Nie znaleziono dzienników", + "action-data":"Dane akcji", + "failure-details":"Szczegóły niepowodzenia", + "search":"Szukaj w dziennikach audytu", + "clear-search":"Wyczyść wyszukiwanie", + "type-assigned-from-tenant":"Przypisano od dzierżawcy", + "type-assigned-to-tenant":"Przypisano do dzierżawcy", + "type-provision-success":"Pomyślne przygotowanie urządzenia", + "type-provision-failure":"Niepowodzenie przygotowania urządzenia", + "type-timeseries-updated":"Zaktualizowano telemetrię", + "type-timeseries-deleted":"Usunięto telemetrię", + "type-sms-sent":"Wysłano SMS" + }, + "confirm-on-exit":{ + "message":"Masz niezapisane zmiany. Czy na pewno chcesz opuścić tę stronę?", + "html-message":"Masz niezapisane zmiany.
Czy na pewno chcesz opuścić tę stronę?", + "title":"Niezapisane zmiany" + }, + "contact":{ + "country":"Kraj", + "city":"Miasto", + "state":"Stan / Prowincja", + "postal-code":"Kod pocztowy", + "postal-code-invalid":"Nieprawidłowy format kodu pocztowego.", + "address":"Adres", + "address2":"Adres 2", + "phone":"Telefon", + "email":"Email", + "no-address":"Brak adresu", + "state-max-length":"Długość stanu powinna być mniejsza niż 256", + "phone-max-length":"Numer telefonu powinien być krótszy niż 256", + "city-max-length":"Nazwa miasta powinna być krótsza niż 256" + }, + "common":{ + "username":"Nazwa użytkownika", + "password":"Hasło", + "enter-username":"Wprowadź nazwę użytkownika", + "enter-password":"Wprowadź hasło", + "enter-search":"Wprowadź wyszukiwanie", + "created-time":"Czas utworzenia", + "loading":"Ładowanie...", + "proceed":"Kontynuuj", + "open-details-page":"Otwórz stronę szczegółów", + "not-found":"Nie znaleziono", + "documentation":"Dokumentacja" + }, + "content-type":{ + "json":"Json", + "text":"Tekst", + "binary":"Binarny (Base64)" + }, + "customer":{ + "customer":"Klient", + "customers":"Klienci", + "management":"Zarządzanie klientami", + "dashboard":"Pulpit nawigacyjny klienta", + "dashboards":"Pulpity nawigacyjne klienta", + "devices":"Urządzenia klienta", + "entity-views":"Widoki encji klienta", + "assets":"Aktywa klienta", + "public-dashboards":"Publiczne pulpity nawigacyjne", + "public-devices":"Publiczne urządzenia", + "public-assets":"Publiczne aktywa", + "public-edges":"Publiczne krawędzie", + "public-entity-views":"Publiczne widoki encji", + "add":"Dodaj klienta", + "delete":"Usuń klienta", + "manage-customer-users":"Zarządzaj użytkownikami klienta", + "manage-customer-devices":"Zarządzaj urządzeniami klienta", + "manage-customer-dashboards":"Zarządzaj pulpitami nawigacyjnymi klienta", + "manage-public-devices":"Zarządzaj publicznymi urządzeniami", + "manage-public-dashboards":"Zarządzaj publicznymi pulpitami nawigacyjnymi", + "manage-customer-assets":"Zarządzaj aktywami klienta", + "manage-public-assets":"Zarządzaj publicznymi aktywami", + "manage-customer-edges":"Zarządzaj krawędziami klienta", + "manage-public-edges":"Zarządzaj publicznymi krawędziami", + "add-customer-text":"Dodaj nowego klienta", + "no-customers-text":"Nie znaleziono klientów", + "customer-details":"Szczegóły klienta", + "delete-customer-title":"Czy na pewno chcesz usunąć klienta '{{customerTitle}}'?", + "delete-customer-text":"Uważaj, po potwierdzeniu klient i wszystkie powiązane dane staną się nieodwracalne.", + "delete-customers-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 klienta} other {# klientów} }?", + "delete-customers-action-title":"Usuń { count, plural, =1 {1 klienta} other {# klientów} }", + "delete-customers-text":"Uważaj, po potwierdzeniu wszyscy wybrani klienci oraz wszystkie powiązane dane staną się nieodwracalne.", + "manage-users":"Zarządzaj użytkownikami", + "manage-assets":"Zarządzaj aktywami", + "manage-devices":"Zarządzaj urządzeniami", + "manage-dashboards":"Zarządzaj pulpitami nawigacyjnymi", + "title":"Tytuł", + "title-required":"Tytuł jest wymagany.", + "title-max-length":"Tytuł powinien być krótszy niż 256", + "description":"Opis", + "details":"Szczegóły", + "events":"Wydarzenia", + "copyId":"Kopiuj ID klienta", + "idCopiedMessage":"ID klienta zostało skopiowane do schowka", + "select-customer":"Wybierz klienta", + "no-customers-matching":"Nie znaleziono klientów pasujących do '{{entity}}'.", + "customer-required":"Klient jest wymagany", + "select-default-customer":"Wybierz domyślnego klienta", + "default-customer":"Domyślny klient", + "default-customer-required":"Domyślny klient jest wymagany do debugowania pulpitu nawigacyjnego na poziomie dzierżawcy", + "search":"Szukaj klientów", + "selected-customers":"{ count, plural, =1 {1 klient} other {# klientów} } wybrano", + "edges":"Instancje krawędzi klienta", + "manage-edges":"Zarządzaj krawędziami" + }, + "date":{ + "last-update-n-ago":"Ostatnia aktualizacja N temu", + "last-update-n-ago-text":"Ostatnia aktualizacja {{ agoText }}", + "custom-date":"Niestandardowa data", + "format":"Format", + "preview":"Podgląd" + }, + "datetime":{ + "date-from":"Data od", + "time-from":"Czas od", + "date-to":"Data do", + "time-to":"Czas do" + }, + "dashboard":{ + "dashboard":"Panel", + "dashboards":"Panele", + "management":"Zarządzanie panelami", + "view-dashboards":"Przeglądaj panele", + "add":"Dodaj panel", + "assign-dashboard-to-customer":"Przypisz panel(y) do klienta", + "assign-dashboard-to-customer-text":"Wybierz panele do przypisania do klienta", + "assign-dashboard-to-edge-title":"Przypisz panel(y) do urządzenia", + "assign-to-customer-text":"Wybierz klienta, do którego przypisane będą panele", + "assign-to-customer":"Przypisz klientowi", + "unassign-from-customer":"Odłącz od klienta", + "make-public":"Udostępnij panel publicznie", + "make-private":"Ustaw panel jako prywatny", + "manage-assigned-customers":"Zarządzaj przypisanymi klientami", + "assigned-customers":"Przypisani klienci", + "assign-to-customers":"Przypisz panele klientom", + "assign-to-customers-text":"Wybierz klientów, do których przypisane będą panele", + "unassign-from-customers":"Odłącz panele od klientów", + "unassign-from-customers-text":"Wybierz klientów, od których odłączone zostaną panele", + "no-dashboards-text":"Nie znaleziono paneli", + "no-widgets":"Brak skonfigurowanych widżetów", + "add-widget":"Dodaj nowy widżet", + "add-widget-button-text":"Dodaj widżet", + "title":"Tytuł", + "image":"Obraz panelu", + "mobile-app-settings":"Ustawienia aplikacji mobilnej", + "mobile-order":"Kolejność panelu w aplikacji mobilnej", + "mobile-hide":"Ukryj panel w aplikacji mobilnej", + "update-image":"Aktualizuj obraz panelu", + "take-screenshot":"Zrób zrzut ekranu", + "select-widget-title":"Wybierz widżet", + "select-widget-value":"{{title}}: wybierz widżet", + "select-widget-subtitle":"Lista dostępnych typów widżetów", + "delete":"Usuń panel", + "title-required":"Tytuł jest wymagany.", + "title-max-length":"Tytuł powinien mieć mniej niż 256 znaków", + "description":"Opis", + "details":"Szczegóły", + "dashboard-details":"Szczegóły panelu", + "add-dashboard-text":"Dodaj nowy panel", + "assign-dashboards":"Przypisz panele", + "assign-new-dashboard":"Przypisz nowy panel", + "assign-dashboards-text":"Przypisz { count, plural, =1 {1 panel} other {# panele} } do klientów", + "unassign-dashboards-action-text":"Odłącz { count, plural, =1 {1 panel} other {# panele} } od klientów", + "delete-dashboards":"Usuń panele", + "unassign-dashboards":"Odłącz panele", + "unassign-dashboards-action-title":"Odłącz { count, plural, =1 {1 panel} other {# panele} } od klienta", + "delete-dashboard-title":"Czy na pewno chcesz usunąć panel '{{dashboardTitle}}'?", + "delete-dashboard-text":"Bądź ostrożny, po potwierdzeniu panel i wszystkie związane z nim dane staną się nieodwracalnie utracone.", + "delete-dashboards-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 panel} other {# panele} }?", + "delete-dashboards-action-title":"Usuń { count, plural, =1 {1 panel} other {# panele} }", + "delete-dashboards-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane panele zostaną usunięte, a wszystkie związane z nimi dane staną się nieodwracalnie utracone.", + "unassign-dashboard-title":"Czy na pewno chcesz odłączyć panel '{{dashboardTitle}}'?", + "unassign-dashboard-text":"Po potwierdzeniu panel zostanie odłączony i nie będzie dostępny dla klienta.", + "unassign-dashboard":"Odłącz panel", + "unassign-dashboards-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 panel} other {# panele} }?", + "unassign-dashboards-text":"Po potwierdzeniu wszystkie wybrane panele zostaną odłączone i nie będą dostępne dla klienta.", + "public-dashboard-title":"Panel jest teraz publiczny", + "public-dashboard-text":"Twój panel {{dashboardTitle}} jest teraz publiczny i dostępny za pomocą następującego publicznego linku:", + "public-dashboard-notice":"Uwaga: Nie zapomnij udostępnić powiązanych urządzeń publicznie, aby uzyskać do nich dostęp.", + "make-private-dashboard-title":"Czy na pewno chcesz uczynić panel '{{dashboardTitle}}' prywatnym?", + "make-private-dashboard-text":"Po potwierdzeniu panel zostanie uczyniony prywatnym i nie będzie dostępny dla innych.", + "make-private-dashboard":"Ustaw panel jako prywatny", + "socialshare-text":"'{{dashboardTitle}}' powered by ThingsBoard", + "socialshare-title":"'{{dashboardTitle}}' powered by ThingsBoard", + "select-dashboard":"Wybierz panel", + "no-dashboards-matching":"Nie znaleziono paneli pasujących do '{{entity}}'", + "dashboard-required":"Panel jest wymagany.", + "select-existing":"Wybierz istniejący panel", + "create-new":"Utwórz nowy panel", + "new-dashboard-title":"Tytuł nowego panelu", + "open-dashboard":"Otwórz panel", + "set-background":"Ustaw tło", + "background-color":"Kolor tła", + "background-image":"Obraz tła", + "background-size-mode":"Tryb rozmiaru tła", + "no-image":"Brak wybranego obrazu", + "empty-image":"Brak obrazu", + "drop-image":"Upuść obraz lub kliknij, aby wybrać plik do przesłania.", + "maximum-upload-file-size":"Maksymalny rozmiar pliku do przesłania: {{ size }}", + "cannot-upload-file":"Nie można przesłać pliku", + "settings":"Ustawienia", + "layout-settings":"Ustawienia układu", + "columns-count":"Liczba kolumn", + "columns-count-required":"Liczba kolumn jest wymagana.", + "min-columns-count-message":"Dozwolona jest tylko minimalna liczba kolumn wynosząca 10.", + "max-columns-count-message":"Dozwolona jest tylko maksymalna liczba kolumn wynosząca 1000.", + "widgets-margins":"Margines między widżetami", + "margin-required":"Wartość marginesu jest wymagana.", + "min-margin-message":"Dozwolona jest tylko wartość minimalna marginesu wynosząca 0.", + "max-margin-message":"Dozwolona jest tylko wartość maksymalna marginesu wynosząca 50.", + "horizontal-margin":"Margines poziomy", + "horizontal-margin-required":"Wartość marginesu poziomego jest wymagana.", + "min-horizontal-margin-message":"Dozwolona jest tylko wartość minimalna marginesu poziomego wynosząca 0.", + "max-horizontal-margin-message":"Dozwolona jest tylko wartość maksymalna marginesu poziomego wynosząca 50.", + "vertical-margin":"Margines pionowy", + "vertical-margin-required":"Wartość marginesu pionowego jest wymagana.", + "min-vertical-margin-message":"Dozwolona jest tylko wartość minimalna marginesu pionowego wynosząca 0.", + "max-vertical-margin-message":"Dozwolona jest tylko wartość maksymalna marginesu pionowego wynosząca 50.", + "apply-outer-margin":"Zastosuj margines do boków układu", + "autofill-height":"Automatycznie wypełnij wysokość układu", + "mobile-layout":"Ustawienia układu na urządzenia mobilne", + "mobile-row-height":"Wysokość rzędu na urządzenia mobilne, px", + "mobile-row-height-required":"Wartość wysokości rzędu na urządzenia mobilne jest wymagana.", + "min-mobile-row-height-message":"Dozwolona jest tylko minimalna wartość wysokości rzędu na urządzenia mobilne wynosząca 5 pikseli.", + "max-mobile-row-height-message":"Dozwolona jest tylko maksymalna wartość wysokości rzędu na urządzenia mobilne wynosząca 200 pikseli.", + "title-settings":"Ustawienia tytułu", + "display-title":"Wyświetl tytuł panelu", + "title-color":"Kolor tytułu", + "toolbar-settings":"Ustawienia paska narzędziowego", + "hide-toolbar":"Ukryj pasek narzędziowy", + "toolbar-always-open":"Zawsze otwarty pasek narzędziowy", + "display-dashboards-selection":"Wyświetl wybór paneli", + "display-entities-selection":"Wyświetl wybór jednostek", + "display-filters":"Wyświetl filtry", + "display-dashboard-timewindow":"Wyświetl okno czasowe panelu", + "display-dashboard-export":"Wyświetl eksport panelu", + "display-update-dashboard-image":"Wyświetl aktualizację obrazu panelu", + "dashboard-logo-settings":"Ustawienia logo panelu", + "display-dashboard-logo":"Wyświetl logo w trybie pełnoekranowym panelu", + "dashboard-logo-image":"Obraz logo panelu", + "advanced-settings":"Zaawansowane ustawienia", + "dashboard-css":"CSS panelu", + "import":"Importuj panel", + "export":"Eksportuj panel", + "export-failed-error":"Nie można wyeksportować panelu: {{error}}", + "create-new-dashboard":"Utwórz nowy panel", + "dashboard-file":"Plik panelu", + "invalid-dashboard-file-error":"Nie można zaimportować panelu: Nieprawidłowa struktura danych panelu.", + "dashboard-import-missing-aliases-title":"Skonfiguruj aliasy używane przez importowany panel", + "create-new-widget":"Utwórz nowy widżet", + "import-widget":"Importuj widżet", + "widget-file":"Plik widżetu", + "invalid-widget-file-error":"Nie można zaimportować widżetu: Nieprawidłowa struktura danych widżetu.", + "widget-import-missing-aliases-title":"Skonfiguruj aliasy używane przez importowany widżet", + "open-toolbar":"Otwórz pasek narzędziowy panelu", + "close-toolbar":"Zamknij pasek narzędziowy panelu", + "configuration-error":"Błąd konfiguracji", + "alias-resolution-error-title":"Błąd konfiguracji aliasów panelu", + "invalid-aliases-config":"Nie można znaleźć żadnych urządzeń pasujących do niektórych filtrów aliasów.
Skontaktuj się z administratorem w celu rozwiązania tego problemu.", + "select-devices":"Wybierz urządzenia", + "assignedToCustomer":"Przypisane do klienta", + "assignedToCustomers":"Przypisane do klientów", + "public":"Publiczne", + "copyId":"Skopiuj identyfikator panelu", + "idCopiedMessage":"Identyfikator panelu został skopiowany do schowka", + "public-link":"Publiczny link", + "copy-public-link":"Skopiuj publiczny link", + "public-link-copied-message":"Publiczny link do panelu został skopiowany do schowka", + "manage-states":"Zarządzaj stanami panelu", + "states":"Stany panelu", + "states-short":"Stany", + "search-states":"Szukaj stanów panelu", + "selected-states":"{ count, plural, =1 {1 stan panelu} other {# stany panelu} } wybranych", + "edit-state":"Edytuj stan panelu", + "delete-state":"Usuń stan panelu", + "add-state":"Dodaj stan panelu", + "no-states-text":"Nie znaleziono stanów", + "state":"Stan panelu", + "state-name":"Nazwa", + "state-name-required":"Wymagana jest nazwa stanu panelu.", + "state-id":"Identyfikator stanu", + "state-id-required":"Wymagane jest Identyfikator stanu panelu.", + "state-id-exists":"Stan panelu o tym samym identyfikatorze już istnieje.", + "is-root-state":"Główny stan", + "delete-state-title":"Usuń stan panelu", + "delete-state-text":"Czy na pewno chcesz usunąć stan panelu o nazwie '{{stateName}}'?", + "show-details":"Pokaż szczegóły", + "hide-details":"Ukryj szczegóły", + "select-state":"Wybierz docelowy stan", + "state-controller":"Kontroler stanu", + "state-controller-default":"statyczny (przestarzały)", + "search":"Szukaj paneli", + "selected-dashboards":"{ count, plural, =1 {1 panel} other {# panele} } wybranych", + "home-dashboard":"Panel główny", + "home-dashboard-hide-toolbar":"Ukryj pasek narzędzi panelu głównego", + "unassign-dashboard-from-edge-text":"Po potwierdzeniu panel zostanie odłączony i nie będzie dostępny dla krawędzi.", + "unassign-dashboards-from-edge-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 panel} other {# panele} }?", + "unassign-dashboards-from-edge-text":"Po potwierdzeniu wszystkie wybrane panele zostaną odłączone i nie będą dostępne dla krawędzi.", + "assign-dashboard-to-edge":"Przypisz panele do krawędzi", + "assign-dashboard-to-edge-text":"Proszę wybrać panele do przypisania do krawędzi", + "non-existent-dashboard-state-error":"Stan panelu o identyfikatorze \"{{ stateId }}\" nie istnieje", + "edit-mode":"Tryb edycji", + "duplicate-state-action":"Zduplikuj stan" + }, + "datakey":{ + "settings":"Ustawienia", + "general":"Ogólne", + "advanced":"Zaawansowane", + "key":"Klucz", + "label":"Etykieta", + "color":"Kolor", + "units":"Symbol specjalny do wyświetlenia obok wartości", + "decimals":"Liczba cyfr po przecinku", + "data-generation-func":"Funkcja generacji danych", + "use-data-post-processing-func":"Użyj funkcji przetwarzania danych po generacji", + "configuration":"Konfiguracja klucza danych", + "timeseries":"Szereg czasowy", + "attributes":"Atrybuty", + "entity-field":"Pole encji", + "alarm":"Pola alarmowe", + "timeseries-required":"Wymagane są serie czasowe encji.", + "timeseries-or-attributes-required":"Wymagane są serie czasowe/cechy encji.", + "alarm-fields-timeseries-or-attributes-required":"Wymagane są pola alarmowe lub serie czasowe/cechy encji.", + "maximum-timeseries-or-attributes":"Maksymalnie { count, plural, =1 {1 seria czasowa/cecha jest dozwolona.} other {# serie czasowe/cechy są dozwolone} }", + "alarm-fields-required":"Wymagane są pola alarmowe.", + "function-types":"Typy funkcji", + "function-type":"Typ funkcji", + "function-types-required":"Wymagane są typy funkcji.", + "data-keys":"Klucze danych", + "data-key":"Klucz danych", + "data-keys-required":"Wymagane są klucze danych.", + "data-key-required":"Wymagany jest klucz danych.", + "alarm-keys":"Klucze danych alarmowych", + "alarm-key":"Klucz danych alarmowych", + "alarm-key-functions":"Funkcje klucza alarmowego", + "alarm-key-function":"Funkcja klucza alarmowego", + "latest-keys":"Ostatnie klucze danych", + "latest-key":"Ostatni klucz danych", + "latest-key-functions":"Funkcje ostatniego klucza", + "latest-key-function":"Funkcja ostatniego klucza", + "timeseries-keys":"Klucze danych szeregów czasowych", + "timeseries-key":"Klucz danych szeregów czasowych", + "timeseries-key-functions":"Funkcje klucza szeregów czasowych", + "timeseries-key-function":"Funkcja klucza szeregów czasowych", + "maximum-function-types":"Maksymalnie { count, plural, =1 {1 typ funkcji jest dozwolony.} other {# typy funkcji są dozwolone} }", + "time-description":"znacznik czasu bieżącej wartości;", + "value-description":"bieżąca wartość;", + "prev-value-description":"wynik poprzedniego wywołania funkcji;", + "time-prev-description":"znacznik czasu poprzedniej wartości;", + "prev-orig-value-description":"oryginalna poprzednia wartość;", + "aggregation":"Agregacja", + "aggregation-type-hint-common":"Ze względów wydajnościowych obliczenia wartości agregowanej są dostępne tylko dla stałych interwałów czasowych, takich jak \"bieżący dzień\", \"bieżący miesiąc\", itp., i nie są dostępne dla interwałów przesuwnych, takich jak 'ostatnie 30 minut' lub 'ostatnie 24 godziny'.", + "aggregation-type-none-hint":"Weź ostatnią wartość.", + "aggregation-type-min-hint":"Znajdź minimalną wartość między punktami danych w wybranym oknie czasowym.", + "aggregation-type-max-hint":"Znajdź maksymalną wartość między punktami danych w wybranym oknie czasowym.", + "aggregation-type-avg-hint":"Oblicz średnią wartość między punktami danych w wybranym oknie czasowym.", + "aggregation-type-sum-hint":"Dodaj wszystkie wartości punktów danych w wybranym oknie czasowym.", + "aggregation-type-count-hint":"Całkowita liczba punktów danych w wybranym oknie czasowym.", + "delta-calculation":"Obliczenia delta", + "enable-delta-calculation":"Włącz obliczenia delta", + "enable-delta-calculation-hint":"Po włączeniu, wartość klucza danych jest obliczana na podstawie wartości agregowanych w wybranym oknie czasowym i okresie porównawczym. Ze względów wydajnościowych obliczenia delta są dostępne tylko dla okien czasowych historycznych, a nie dla wartości czasu rzeczywistego. Na przykład możesz obliczyć różnicę między zużyciem energii wczoraj a zużyciem energii przedwczoraj.", + "delta-calculation-result":"Wynik obliczeń delta", + "delta-calculation-result-previous-value":"Poprzednia wartość", + "delta-calculation-result-delta-absolute":"Delta (bezwzględna)", + "delta-calculation-result-delta-percent":"Delta (procentowa)", + "source":"Źródło", + "latest":"Najnowsze", + "latest-value":"Najnowsza wartość", + "delta":"delta", + "percent":"procent", + "absolute":"bezwzględna" + }, + "datasource":{ + "type":"Typ źródła danych", + "name":"Nazwa", + "label":"Etykieta", + "add-datasource-prompt":"Proszę dodać źródło danych" + }, + "details":{ + "details":"Szczegóły", + "edit-mode":"Tryb edycji", + "edit-json":"Edytuj JSON", + "toggle-edit-mode":"Przełącz tryb edycji" + }, + "device":{ + "device":"Urządzenie", + "device-required":"Urządzenie jest wymagane.", + "devices":"Urządzenia", + "management":"Zarządzanie urządzeniem", + "view-devices":"Zobacz urządzenia", + "device-alias":"Alias urządzenia", + "device-type-max-length":"Typ urządzenia powinien mieć mniej niż 256 znaków", + "aliases":"Aliasy urządzeń", + "no-alias-matching":"'{{alias}}' nie znaleziono.", + "no-aliases-found":"Nie znaleziono aliasów.", + "no-key-matching":"'{{key}}' nie znaleziono.", + "no-keys-found":"Nie znaleziono kluczy.", + "create-new-alias":"Utwórz nowy!", + "create-new-key":"Utwórz nowy!", + "duplicate-alias-error":"Znaleziono duplikat aliasu '{{alias}}'.
Aliasy urządzeń muszą być unikalne w ramach panelu.", + "configure-alias":"Konfiguruj alias '{{alias}}'", + "no-devices-matching":"Nie znaleziono urządzeń pasujących do '{{entity}}'.", + "alias":"Alias", + "alias-required":"Alias urządzenia jest wymagany.", + "remove-alias":"Usuń alias urządzenia", + "add-alias":"Dodaj alias urządzenia", + "name-starts-with":"Wyrażenie nazwy urządzenia", + "help-text":"Użyj '%' według potrzeb: '%device_name_contains%', '%device_name_ends%', 'device_starts_with'.", + "device-list":"Lista urządzeń", + "use-device-name-filter":"Użyj filtra", + "device-list-empty":"Brak wybranych urządzeń.", + "device-name-filter-required":"Filtr nazwy urządzenia jest wymagany.", + "device-name-filter-no-device-matched":"Nie znaleziono urządzeń rozpoczynających się od '{{device}}'.", + "add":"Dodaj urządzenie", + "assign-to-customer":"Przypisz do klienta", + "assign-device-to-customer":"Przypisz urządzenia do klienta", + "assign-device-to-customer-text":"Proszę wybrać urządzenia do przypisania do klienta", + "assign-device-to-edge-title":"Przypisz urządzenia do krawędzi", + "assign-device-to-edge-text":"Proszę wybrać urządzenia do przypisania do krawędzi", + "make-public":"Udostępnij publicznie", + "make-private":"Udostępnij prywatnie", + "no-devices-text":"Nie znaleziono urządzeń", + "assign-to-customer-text":"Proszę wybrać klienta, do którego przypisane będą urządzenia", + "device-details":"Szczegóły urządzenia", + "add-device-text":"Dodaj nowe urządzenie", + "credentials":"Poświadczenia", + "manage-credentials":"Zarządzaj poświadczeniami", + "delete":"Usuń urządzenie", + "assign-devices":"Przypisz urządzenia", + "assign-devices-text":"Przypisz { count, plural, =1 {1 urządzenie} other {# urządzeń} } do klienta", + "delete-devices":"Usuń urządzenia", + "unassign-from-customer":"Odłącz od klienta", + "unassign-devices":"Odłącz urządzenia", + "unassign-devices-action-title":"Odłącz { count, plural, =1 {1 urządzenie} other {# urządzeń} } od klienta", + "unassign-device-from-edge-title":"Czy na pewno chcesz odłączyć urządzenie '{{deviceName}}'?", + "unassign-device-from-edge-text":"Po potwierdzeniu urządzenie zostanie odłączone i nie będzie dostępne przez krawędź.", + "unassign-devices-from-edge":"Odłącz urządzenia od krawędzi", + "assign-new-device":"Przypisz nowe urządzenie", + "make-public-device-title":"Czy na pewno chcesz udostępnić publicznie urządzenie '{{deviceName}}'?", + "make-public-device-text":"Po potwierdzeniu urządzenie i wszystkie jego dane będą dostępne publicznie dla innych.", + "make-private-device-title":"Czy na pewno chcesz udostępnić prywatnie urządzenie '{{deviceName}}'?", + "make-private-device-text":"Po potwierdzeniu urządzenie i wszystkie jego dane będą dostępne prywatnie i nie będą dostępne dla innych.", + "view-credentials":"Zobacz poświadczenia", + "delete-device-title":"Czy na pewno chcesz usunąć urządzenie '{{deviceName}}'?", + "delete-device-text":"Bądź ostrożny, po potwierdzeniu urządzenie i wszystkie powiązane dane staną się nieodwracalnie utracone.", + "delete-devices-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 urządzenie} other {# urządzeń} }?", + "delete-devices-action-title":"Usuń { count, plural, =1 {1 urządzenie} other {# urządzeń} }", + "delete-devices-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane urządzenia zostaną usunięte, a wszystkie powiązane dane staną się nieodwracalnie utracone.", + "unassign-device-title":"Czy na pewno chcesz odłączyć urządzenie '{{deviceName}}'?", + "unassign-device-text":"Po potwierdzeniu urządzenie zostanie odłączone i nie będzie dostępne przez klienta.", + "unassign-device":"Odłącz urządzenie", + "unassign-devices-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 urządzenie} other {# urządzeń} }?", + "unassign-devices-text":"Po potwierdzeniu wszystkie wybrane urządzenia zostaną odłączone i nie będą dostępne przez klienta.", + "device-credentials":"Poświadczenia urządzenia", + "loading-device-credentials":"Ładowanie poświadczeń urządzenia...", + "credentials-type":"Typ poświadczeń", + "access-token":"Token dostępu", + "access-token-required":"Token dostępu jest wymagany.", + "access-token-invalid":"Długość tokenu dostępu musi wynosić od 1 do 32 znaków.", + "certificate-pem-format":"Certyfikat w formacie PEM", + "certificate-pem-format-required":"Certyfikat jest wymagany.", + "copy-access-token":"Skopiuj token dostępu", + "copy-certificate":"Skopiuj certyfikat", + "copy-client-id":"Skopiuj identyfikator klienta", + "copy-user-name":"Skopiuj nazwę użytkownika", + "copy-password":"Skopiuj hasło", + "generate-client-id":"Generuj identyfikator klienta", + "generate-user-name":"Generuj nazwę użytkownika", + "generate-password":"Generuj hasło", + "generate-access-token":"Generuj token dostępu", + "lwm2m-security-config":{ + "identity":"Identyfikator klienta", + "identity-required":"Identyfikator klienta jest wymagany.", + "identity-tooltip":"Identyfikator PSK to dowolny identyfikator PSK o długości do 128 bajtów, zgodny z opisem w standardzie [RFC7925].\nIdentyfikator PSK MUSI być najpierw przekształcony w ciąg znaków, a następnie zakodowany na bajty przy użyciu UTF-8.", + "client-key":"Klucz klienta", + "client-key-required":"Klucz klienta jest wymagany.", + "client-key-tooltip-prk":"Klucz publiczny RPK lub identyfikator muszą być zgodne ze standardem [RFC7250] i zakodowane w formacie Base64!", + "client-key-tooltip-psk":"Klucz PSK musi być zgodny ze standardem [RFC4279] i formatem HexDec: 32, 64, 128 znaków!", + "endpoint":"Nazwa klienta Endpoint", + "endpoint-required":"Nazwa klienta Endpoint jest wymagana.", + "client-public-key":"Klucz publiczny klienta", + "client-public-key-hint":"Jeśli klucz publiczny klienta jest pusty, używane będzie zaufane certyfikat", + "client-public-key-tooltip":"Klucz publiczny X509 musi być w formacie zakodowanym DER X509v3 i obsługiwać wyłącznie algorytm EC, a następnie zakodowany w formacie Base64!", + "mode":"Tryb konfiguracji zabezpieczeń", + "client-tab":"Konfiguracja zabezpieczeń klienta", + "client-certificate":"Certyfikat klienta", + "bootstrap-tab":"Klient rozruchowy", + "bootstrap-server":"Serwer rozruchowy", + "lwm2m-server":"Serwer LwM2M", + "client-publicKey-or-id":"Klucz publiczny klienta lub Identyfikator", + "client-publicKey-or-id-required":"Klucz publiczny klienta lub Identyfikator są wymagane.", + "client-publicKey-or-id-tooltip-psk":"Identyfikator PSK to dowolny identyfikator PSK o długości do 128 bajtów, zgodny z opisem w standardzie [RFC7925].\nIdentyfikator PSK MUSI być najpierw przekształcony w ciąg znaków, a następnie zakodowany na bajty przy użyciu UTF-8.", + "client-publicKey-or-id-tooltip-rpk":"Klucz publiczny RPK lub identyfikator muszą być zgodne ze standardem [RFC7250] i zakodowane w formacie Base64!", + "client-publicKey-or-id-tooltip-x509":"Klucz publiczny X509 musi być w formacie zakodowanym DER X509v3 i obsługiwać wyłącznie algorytm EC, a następnie zakodowany w formacie Base64!", + "client-secret-key":"Tajny klucz klienta", + "client-secret-key-required":"Tajny klucz klienta jest wymagany.", + "client-secret-key-tooltip-psk":"Klucz PSK musi być zgodny ze standardem [RFC4279] i formatem HexDec: 32, 64, 128 znaków!", + "client-secret-key-tooltip-prk":"Tajny klucz RPK musi być w formacie PKCS_8 (kodowanie DER, standard [RFC5958]) i zakodowany w formacie Base64!", + "client-secret-key-tooltip-x509":"Tajny klucz X509 musi być w formacie PKCS_8 (kodowanie DER, standard [RFC5958]) i zakodowany w formacie Base64!" + }, + "client-id":"ID klienta", + "client-id-pattern":"Zawiera nieprawidłowy znak.", + "user-name":"Nazwa użytkownika", + "user-name-required":"Nazwa użytkownika jest wymagana.", + "client-id-or-user-name-necessary":"ID klienta i/lub Nazwa użytkownika są wymagane", + "password":"Hasło", + "secret":"Sekret", + "secret-required":"Sekret jest wymagany.", + "device-type":"Profil urządzenia", + "device-type-required":"Typ urządzenia jest wymagany.", + "select-device-type":"Wybierz typ urządzenia", + "enter-device-type":"Wprowadź profil urządzenia", + "any-device":"Dowolne urządzenie", + "no-device-types-matching":"Nie znaleziono profili urządzeń pasujących do '{{entitySubtype}}'.", + "device-type-list-empty":"Nie wybrano profili urządzeń!", + "device-profile-type-list-empty":"Należy wybrać co najmniej jeden profil urządzenia.", + "device-types":"Profile urządzeń", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "name-max-length":"Nazwa powinna być krótsza niż 256", + "label-max-length":"Etykieta powinna być krótsza niż 256", + "description":"Opis", + "label":"Etykieta", + "events":"Zdarzenia", + "details":"Szczegóły", + "copyId":"Kopiuj ID urządzenia", + "copyAccessToken":"Kopiuj token dostępu", + "copy-mqtt-authentication":"Kopiuj poświadczenia MQTT", + "idCopiedMessage":"ID urządzenia zostało skopiowane do schowka", + "accessTokenCopiedMessage":"Token dostępu do urządzenia został skopiowany do schowka", + "mqtt-authentication-copied-message":"Poświadczenia MQTT urządzenia zostały skopiowane do schowka", + "assignedToCustomer":"Przypisane do klienta", + "unable-delete-device-alias-title":"Nie można usunąć aliasu urządzenia", + "unable-delete-device-alias-text":"Alias urządzenia '{{deviceAlias}}' nie może być usunięty, ponieważ jest używany przez następujące widżety:
{{widgetsList}}", + "is-gateway":"Jest bramą", + "overwrite-activity-time":"Nadpisz czas aktywności podłączonego urządzenia", + "device-filter":"Filtr urządzenia", + "device-filter-title":"Filtr urządzenia", + "filter-title":"Filtr", + "device-state":"Stan urządzenia", + "state":"Stan", + "any":"Dowolny", + "active":"Aktywny", + "inactive":"Nieaktywny", + "public":"Publiczny", + "device-public":"Urządzenie jest publiczne", + "select-device":"Wybierz urządzenie", + "import":"Importuj urządzenie", + "device-file":"Plik urządzenia", + "search":"Wyszukaj urządzenia", + "selected-devices":"{ count, plural, =1 {1 urządzenie} other {# urządzeń} } wybranych", + "device-configuration":"Konfiguracja urządzenia", + "transport-configuration":"Konfiguracja transportu", + "wizard":{ + "device-details":"Szczegóły urządzenia" + }, + "unassign-devices-from-edge-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 urządzenie} other {# urządzeń} }?", + "unassign-devices-from-edge-text":"Po potwierdzeniu wszystkie wybrane urządzenia zostaną odłączone i nie będą dostępne dla krawędzi.", + "time":"Czas", + "connectivity":{ + "check-connectivity":"Sprawdź łączność", + "device-created-check-connectivity":"Urządzenie utworzone. Sprawdźmy łączność!", + "loading-check-connectivity-command":"Ładowanie poleceń sprawdzania łączności...", + "use-following-instructions":"Skorzystaj z poniższych instrukcji, aby wysyłać telemetrię w imieniu urządzenia za pomocą konsoli", + "execute-following-command":"Wykonaj poniższe polecenie", + "install-curl-windows":"Począwszy od systemu Windows 10 b17063, cURL jest dostępny domyślnie", + "install-curl-macos":"Począwszy od Mac OS X 10.2 6C115 (Jaguar), cURL jest dostępny domyślnie", + "install-mqtt-windows":"Użyj instrukcji do pobrania, zainstalowania, skonfigurowania i uruchomienia mosquitto_pub", + "install-coap-client":"Użyj instrukcji do pobrania, zainstalowania, skonfigurowania i uruchomienia klienta coap", + "install-necessary-client-tools":"Zainstaluj niezbędne narzędzia klienta", + "mqtts-x509-command":"Skorzystaj z poniższej dokumentacji, aby połączyć urządzenie za pomocą MQTT z uwierzytelnianiem X509", + "coaps-x509-command":"Skorzystaj z poniższej dokumentacji, aby połączyć urządzenie za pomocą CoAP przez DTLS z uwierzytelnianiem X509", + "snmp-command":"Skorzystaj z poniższej dokumentacji, aby połączyć urządzenie za pomocą SNMP.", + "sparkplug-command":"Skorzystaj z poniższej dokumentacji, aby połączyć urządzenie za pomocą MQTT Sparkplug.", + "lwm2m-command":"Skorzystaj z poniższej dokumentacji, aby połączyć urządzenie za pomocą LWM2M." + } + }, + "asset-profile":{ + "asset-profile":"Profil zasobu", + "asset-profiles":"Profile zasobów", + "all-asset-profiles":"Wszystkie", + "add":"Dodaj profil zasobu", + "edit":"Edytuj profil zasobu", + "asset-profile-details":"Szczegóły profilu zasobu", + "no-asset-profiles-text":"Nie znaleziono profili zasobów", + "search":"Szukaj profili zasobów", + "selected-asset-profiles":"{ count, plural, =1 {1 profil zasobu} other {# profile zasobów} } wybrano", + "no-asset-profiles-matching":"Nie znaleziono profilu zasobu pasującego do '{{entity}}'.", + "asset-profile-required":"Wymagany profil zasobu", + "idCopiedMessage":"Identyfikator profilu zasobu został skopiowany do schowka", + "set-default":"Ustaw jako domyślny profil zasobu", + "delete":"Usuń profil zasobu", + "copyId":"Kopiuj identyfikator profilu zasobu", + "name-max-length":"Nazwa powinna mieć mniej niż 256 znaków", + "new-device-profile-name":"Nazwa profilu zasobu", + "new-device-profile-name-required":"Wymagana jest nazwa profilu zasobu.", + "name":"Nazwa", + "name-required":"Wymagana jest nazwa.", + "image":"Zdjęcie profilu zasobu", + "description":"Opis", + "default":"Domyślny", + "default-rule-chain":"Domyślny łańcuch reguł", + "default-edge-rule-chain":"Domyślny łańcuch reguł na krawędzi", + "default-edge-rule-chain-hint":"Używane na krawędzi jako łańcuch reguł do przetwarzania przychodzących danych dla zasobów tego profilu zasobu", + "mobile-dashboard":"Mobilna konsola", + "mobile-dashboard-hint":"Używane przez aplikację mobilną jako konsola szczegółów zasobu", + "select-queue-hint":"Wybierz z listy rozwijanej.", + "delete-asset-profile-title":"Czy na pewno chcesz usunąć profil zasobu '{{assetProfileName}}'?", + "delete-asset-profile-text":"Bądź ostrożny, po potwierdzeniu profil zasobu i wszystkie związane dane staną się nieodwracalnie utracone.", + "delete-asset-profiles-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 profil zasobu} other {# profile zasobów} }?", + "delete-asset-profiles-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane profile zasobów zostaną usunięte, a wszystkie związane dane staną się nieodwracalnie utracone.", + "set-default-asset-profile-title":"Czy na pewno chcesz ustawić profil zasobu '{{assetProfileName}}' jako domyślny?", + "set-default-asset-profile-text":"Po potwierdzeniu profil zasobu zostanie oznaczony jako domyślny i będzie używany dla nowych zasobów bez określonego profilu.", + "no-asset-profiles-found":"Nie znaleziono profili zasobów.", + "create-new-asset-profile":"Utwórz nowy!", + "create-asset-profile":"Utwórz nowy profil zasobu", + "import":"Importuj profil zasobu", + "export":"Eksportuj profil zasobu", + "export-failed-error":"Nie można wyeksportować profilu zasobu: {{error}}", + "asset-profile-file":"Plik profilu zasobu", + "invalid-asset-profile-file-error":"Nie można zaimportować profilu zasobu: Nieprawidłowa struktura danych profilu zasobu." + }, + "device-profile":{ + "device-profile":"Profil urządzenia", + "device-profiles":"Profile urządzeń", + "all-device-profiles":"Wszystkie", + "add":"Dodaj profil urządzenia", + "edit":"Edytuj profil urządzenia", + "device-profile-details":"Szczegóły profilu urządzenia", + "no-device-profiles-text":"Nie znaleziono profili urządzeń", + "search":"Szukaj profili urządzeń", + "selected-device-profiles":"{ count, plural, =1 {1 profil urządzenia} other {# profile urządzeń} } wybrano", + "no-device-profiles-matching":"Nie znaleziono profilu urządzenia pasującego do '{{entity}}'.", + "device-profile-required":"Wymagany profil urządzenia", + "idCopiedMessage":"Identyfikator profilu urządzenia został skopiowany do schowka", + "set-default":"Ustaw jako domyślny profil urządzenia", + "delete":"Usuń profil urządzenia", + "copyId":"Kopiuj identyfikator profilu urządzenia", + "name-max-length":"Nazwa powinna mieć mniej niż 256 znaków", + "name":"Nazwa", + "name-required":"Wymagana jest nazwa.", + "type":"Typ profilu", + "type-required":"Wymagany jest typ profilu.", + "type-default":"Domyślny", + "image":"Zdjęcie profilu urządzenia", + "transport-type":"Typ transportu", + "transport-type-required":"Wymagany jest typ transportu.", + "transport-type-default":"Domyślny", + "transport-type-default-hint":"Obsługuje podstawowe transporty MQTT, HTTP i CoAP", + "transport-type-mqtt":"MQTT", + "transport-type-mqtt-hint":"Umożliwia zaawansowane ustawienia transportu MQTT", + "transport-type-coap":"CoAP", + "transport-type-coap-hint":"Umożliwia zaawansowane ustawienia transportu CoAP", + "transport-type-lwm2m":"LWM2M", + "transport-type-lwm2m-hint":"Typ transportu LWM2M", + "transport-type-snmp":"SNMP", + "transport-type-snmp-hint":"Określ konfigurację transportu SNMP", + "transport-type-http":"HTTP", + "description":"Opis", + "default":"Domyślny", + "profile-configuration":"Konfiguracja profilu", + "transport-configuration":"Konfiguracja transportu", + "default-rule-chain":"Domyślny łańcuch reguł", + "default-edge-rule-chain":"Domyślny łańcuch reguł na krawędzi", + "default-edge-rule-chain-hint":"Używane na krawędzi jako łańcuch reguł do przetwarzania przychodzących danych dla urządzeń tego profilu urządzenia", + "mobile-dashboard":"Mobilna konsola", + "mobile-dashboard-hint":"Używane przez aplikację mobilną jako konsola szczegółów urządzenia", + "select-queue-hint":"Wybierz z listy rozwijanej.", + "delete-device-profile-title":"Czy na pewno chcesz usunąć profil urządzenia '{{deviceProfileName}}'?", + "delete-device-profile-text":"Bądź ostrożny, po potwierdzeniu profil urządzenia i wszystkie związane dane, w tym powiązane aktualizacje OTA, staną się nieodwracalnie utracone.", + "delete-device-profiles-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 profil urządzenia} other {# profile urządzeń} }?", + "delete-device-profiles-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane profile urządzeń zostaną usunięte, a wszystkie związane dane, w tym powiązane aktualizacje OTA, staną się nieodwracalnie utracone.", + "set-default-device-profile-title":"Czy na pewno chcesz ustawić profil urządzenia '{{deviceProfileName}}' jako domyślny?", + "set-default-device-profile-text":"Po potwierdzeniu profil urządzenia zostanie oznaczony jako domyślny i będzie używany dla nowych urządzeń bez określonego profilu.", + "no-device-profiles-found":"Nie znaleziono profili urządzeń.", + "create-new-device-profile":"Utwórz nowy!", + "mqtt-device-topic-filters":"Filtr tematów urządzenia MQTT", + "mqtt-device-topic-filters-unique":"Filtry tematów urządzenia MQTT muszą być unikalne.", + "mqtt-device-topic-filters-spark-plug":"Węzeł krawędziowy MQTT Sparkplug B (EoN).", + "mqtt-device-topic-filters-spark-plug-hint":"Zezwól na połączenia z węzłami EoN z ładunkiem i formatem tematu Sparkplug B.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names":"Metryki SparkPlug do przechowywania jako atrybuty.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint":"Nazwy metryk SparkPlug, które będą przechowywane jako atrybuty urządzenia. Wszystkie inne metryki będą przechowywane jako telemetria urządzenia.", + "mqtt-device-payload-type":"Typ ładunka urządzenia MQTT", + "mqtt-device-payload-type-json":"JSON", + "mqtt-device-payload-type-proto":"Protobuf", + "mqtt-enable-compatibility-with-json-payload-format":"Włącz zgodność z innymi formatami ładunków.", + "mqtt-enable-compatibility-with-json-payload-format-hint":"Gdy jest włączona, platforma będzie domyślnie używać formatu ładunku Protobuf. Jeśli parsowanie się nie powiedzie, platforma spróbuje użyć formatu ładunku JSON. Przydatne dla wstecznej kompatybilności podczas aktualizacji oprogramowania układowego. Na przykład początkowa wersja oprogramowania używa Json, podczas gdy nowa wersja używa Protobuf. Podczas procesu aktualizacji oprogramowania dla floty urządzeń konieczne jest jednoczesne obsługiwanie zarówno Protobuf, jak i JSON. Tryb zgodności wprowadza niewielki spadek wydajności, więc zaleca się wyłączenie tego trybu, gdy wszystkie urządzenia zostaną zaktualizowane.", + "mqtt-use-json-format-for-default-downlink-topics":"Użyj formatu Json dla domyślnych tematów zwrotnych", + "mqtt-use-json-format-for-default-downlink-topics-hint":"Gdy jest włączone, platforma będzie używać formatu ładunku Json do przesyłania atrybutów i RPC za pomocą następujących tematów: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. Ta ustawienie nie wpływa na subskrypcje atrybutów i rpc wysyłane za pomocą nowych (v2) tematów: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Gdzie $request_id to identyfikator żądania.", + "mqtt-send-ack-on-validation-exception":"Wyślij PUBACK w przypadku niepowodzenia walidacji komunikatu PUBLISH", + "mqtt-send-ack-on-validation-exception-hint":"Domyślnie platforma zakończy sesję MQTT w przypadku niepowodzenia walidacji komunikatu. Gdy jest włączone, platforma wyśle potwierdzenie publikacji zamiast zakończenia sesji.", + "snmp-add-mapping":"Dodaj mapowanie SNMP", + "snmp-mapping-not-configured":"Nie skonfigurowano mapowania OID na szeregi czasowe/telemetrię", + "snmp-timseries-or-attribute-name":"Nazwa szeregów czasowych/cechy do mapowania", + "snmp-timseries-or-attribute-type":"Typ szeregów czasowych/cechy do mapowania", + "snmp-method-pdu-type-get-request":"GetRequest", + "snmp-method-pdu-type-get-next-request":"GetNextRequest", + "snmp-oid":"OID", + "transport-device-payload-type-json":"JSON", + "transport-device-payload-type-proto":"Protobuf", + "mqtt-payload-type-required":"Wymagany jest typ ładunka.", + "coap-device-type":"Typ urządzenia CoAP", + "coap-device-payload-type":"Typ ładunka urządzenia CoAP", + "coap-device-type-required":"Wymagany jest typ urządzenia CoAP.", + "coap-device-type-default":"Domyślny", + "coap-device-type-efento":"Efento NB-IoT", + "support-level-wildcards":"Obsługiwane są pojedyncze [+] i wielopoziomowe [#] znaki wieloznaczne.", + "telemetry-topic-filter":"Filtr tematu telemetrii", + "telemetry-topic-filter-required":"Wymagany jest filtr tematu telemetrii.", + "attributes-topic-filter":"Filtr tematu publikacji atrybutów", + "attributes-subscribe-topic-filter":"Filtr tematu subskrypcji atrybutów", + "attributes-topic-filter-required":"Wymagany jest filtr tematu publikacji atrybutów.", + "attributes-subscribe-topic-filter-required":"Wymagany jest filtr tematu subskrypcji atrybutów", + "telemetry-proto-schema":"Schemat protokołu telemetrii", + "telemetry-proto-schema-required":"Wymagany jest schemat protokołu telemetrii.", + "attributes-proto-schema":"Schemat protokołu atrybutów", + "attributes-proto-schema-required":"Wymagany jest schemat protokołu atrybutów.", + "rpc-response-proto-schema":"Schemat protokołu odpowiedzi RPC", + "rpc-response-proto-schema-required":"Wymagany jest schemat protokołu odpowiedzi RPC.", + "rpc-response-topic-filter":"Filtr tematu odpowiedzi RPC", + "rpc-response-topic-filter-required":"Wymagany jest filtr tematu odpowiedzi RPC.", + "rpc-request-proto-schema":"Schemat protokołu żądania RPC", + "rpc-request-proto-schema-required":"Wymagany jest schemat protokołu żądania RPC.", + "rpc-request-proto-schema-hint":"Wiadomość żądania RPC powinna zawsze zawierać pola: string method = 1; int32 requestId = 2; oraz params = 3 dowolnego typu danych.", + "not-valid-pattern-topic-filter":"Nieprawidłowy filtr tematu wzorca", + "not-valid-single-character":"Nieprawidłowe użycie znaku wieloznacznego jednego poziomu", + "not-valid-multi-character":"Nieprawidłowe użycie znaku wieloznacznego wielu poziomów", + "single-level-wildcards-hint":"[+] nadaje się do dowolnego poziomu filtra tematu. Na przykład: v1/devices/+/telemetry lub +/devices/+/attributes.", + "multi-level-wildcards-hint":"[#] może zastąpić sam filtr tematu i musi być ostatnim symbolem tematu. Na przykład: # lub v1/devices/me/#.", + "alarm-rules":"Zasady alarmowe", + "alarm-rules-with-count":"Zasady alarmowe ({{count}})", + "no-alarm-rules":"Brak skonfigurowanych zasad alarmowych", + "add-alarm-rule":"Dodaj zasadę alarmową", + "edit-alarm-rule":"Edytuj zasadę alarmową", + "alarm-type":"Typ alarmu", + "alarm-type-required":"Wymagany jest typ alarmu.", + "alarm-type-unique":"Typ alarmu musi być unikalny w ramach zasad alarmowych profilu urządzenia.", + "alarm-type-max-length":"Typ alarmu powinien być krótszy niż 256", + "create-alarm-pattern":"Utwórz alarm {{alarmType}}", + "create-alarm-rules":"Utwórz zasady alarmowe", + "no-create-alarm-rules":"Brak skonfigurowanych warunków utworzenia", + "add-create-alarm-rule-prompt":"Proszę dodać warunek utworzenia alarmu", + "clear-alarm-rule":"Wyczyść zasadę alarmową", + "no-clear-alarm-rule":"Brak skonfigurowanego warunku wyczyszczenia", + "add-create-alarm-rule":"Dodaj warunek utworzenia alarmu", + "add-clear-alarm-rule":"Dodaj warunek wyczyszczenia", + "select-alarm-severity":"Wybierz stopień pilności alarmu", + "alarm-severity-required":"Wymagany jest stopień pilności alarmu.", + "condition-duration":"Czas trwania warunku", + "condition-duration-value":"Wartość czasu trwania", + "condition-duration-time-unit":"Jednostka czasu", + "condition-duration-value-range":"Wartość czasu trwania powinna zawierać się w zakresie od 1 do 2147483647.", + "condition-duration-value-pattern":"Wartość czasu trwania powinna być liczbą całkowitą.", + "condition-duration-value-required":"Wymagana jest wartość czasu trwania.", + "condition-duration-time-unit-required":"Wymagana jest jednostka czasu.", + "advanced-settings":"Zaawansowane ustawienia", + "alarm-rule-additional-info":"Dodatkowe informacje", + "edit-alarm-rule-additional-info":"Edytuj dodatkowe informacje", + "alarm-rule-additional-info-placeholder":"Proszę podać swoje uwagi i dostosowania tutaj, aby wyświetlić je w szczegółach alarmu w sekcji Dodatkowe informacje", + "alarm-rule-additional-info-hint":"Wskazówka: użyj ${kluczNazwy} do zastąpienia wartości kluczy atrybutu lub telemetrii używanych w warunku zasady alarmowej.", + "alarm-rule-mobile-dashboard":"Panel nawigacyjny na urządzenia przenośne", + "alarm-rule-mobile-dashboard-hint":"Używane przez aplikację mobilną jako panel nawigacyjny szczegółów alarmu", + "alarm-rule-no-mobile-dashboard":"Nie wybrano panelu nawigacyjnego", + "propagate-alarm":"Rozprzestrzeniaj alarm do powiązanych jednostek", + "alarm-rule-relation-types-list":"Typy relacji do rozprzestrzeniania", + "alarm-rule-relation-types-list-hint":"Jeśli nie zostaną wybrane typy relacji do rozprzestrzeniania, alarmy będą rozprzestrzeniane bez filtrowania według typu relacji.", + "propagate-alarm-to-owner":"Rozprzestrzeniaj alarm do właściciela jednostki (Klienta lub Najemcy)", + "propagate-alarm-to-tenant":"Rozprzestrzeniaj alarm do Najemcy", + "alarm-rule-condition":"Warunek zasady alarmowej", + "enter-alarm-rule-condition-prompt":"Proszę dodać warunek zasady alarmowej", + "edit-alarm-rule-condition":"Edytuj warunek zasady alarmowej", + "device-provisioning":"Proces przydzielania urządzenia", + "provision-strategy":"Strategia przydzielania", + "provision-strategy-required":"Wymagana jest strategia przydzielania.", + "provision-strategy-disabled":"Wyłączone", + "provision-strategy-created-new":"Zezwól na tworzenie nowych urządzeń", + "provision-strategy-check-pre-provisioned":"Sprawdź wcześniej przydzielone urządzenia", + "provision-device-key":"Klucz przydzielania urządzenia", + "provision-device-key-required":"Wymagany jest klucz przydzielania urządzenia.", + "copy-provision-key":"Kopiuj klucz przydzielania", + "provision-key-copied-message":"Klucz przydzielania został skopiowany do schowka", + "provision-device-secret":"Sekret przydzielania urządzenia", + "provision-device-secret-required":"Wymagany jest sekret przydzielania urządzenia.", + "copy-provision-secret":"Kopiuj sekret przydzielania", + "provision-secret-copied-message":"Sekret przydzielania został skopiowany do schowka", + "provision-strategy-x509":{ + "certificate-chain":"Łańcuch certyfikatów X509", + "certificate-chain-hint":"Strategia certyfikatów X.509 jest używana do przydzielania urządzeń za pomocą certyfikatów klientów w dwukierunkowej komunikacji TLS.", + "allow-create-new-devices":"Utwórz nowe urządzenia", + "allow-create-new-devices-hint":"Jeśli zaznaczone, nowe urządzenia zostaną utworzone, a certyfikat klienta będzie używany jako dane uwierzytelniające urządzenia.", + "certificate-value":"Certyfikat w formacie PEM", + "certificate-value-required":"Wymagany jest certyfikat w formacie PEM", + "cn-regex-variable":"Zmienna wyrażenia regularnego CN", + "cn-regex-variable-required":"Wymagana jest zmienna wyrażenia regularnego CN", + "cn-regex-variable-hint":"Wymagane do pobrania nazwy urządzenia z wspólnego nazewnictwa certyfikatu X509 urządzenia." + }, + "condition":"Warunek", + "condition-type":"Typ warunku", + "condition-type-simple":"Prosty", + "condition-type-duration":"Czas trwania", + "condition-during":"Podczas {{during}}", + "condition-during-dynamic":"Podczas \"{{attribute}}\" ({{during}})", + "condition-type-repeating":"Powtarzający się", + "condition-type-required":"Wymagany jest typ warunku.", + "condition-repeating-value":"Liczba zdarzeń", + "condition-repeating-value-range":"Liczba zdarzeń powinna mieścić się w zakresie od 1 do 2147483647.", + "condition-repeating-value-pattern":"Liczba zdarzeń powinna być liczbą całkowitą.", + "condition-repeating-value-required":"Wymagana jest liczba zdarzeń.", + "condition-repeat-times":"Powtarza się { count, plural, =1 {1 raz} other {# razy} }", + "condition-repeat-times-dynamic":"Powtarza się \"{attribute}\" ({ count, plural, =1 {1 raz} other {# razy} })", + "schedule-type":"Typ harmonogramu", + "schedule-type-required":"Wymagany jest typ harmonogramu.", + "schedule":"Harmonogram", + "edit-schedule":"Edytuj harmonogram alarmu", + "schedule-any-time":"Aktywny przez cały czas", + "schedule-specific-time":"Aktywny o określonej godzinie", + "schedule-custom":"Niestandardowy", + "schedule-day":{ + "monday":"Poniedziałek", + "tuesday":"Wtorek", + "wednesday":"Środa", + "thursday":"Czwartek", + "friday":"Piątek", + "saturday":"Sobota", + "sunday":"Niedziela" + }, + "schedule-days":"Dni", + "schedule-time":"Czas", + "schedule-time-from":"Od", + "schedule-time-to":"Do", + "schedule-days-of-week-required":"Należy wybrać przynajmniej jeden dzień tygodnia.", + "create-device-profile":"Utwórz nowy profil urządzenia", + "import":"Importuj profil urządzenia", + "export":"Eksportuj profil urządzenia", + "export-failed-error":"Nie można wyeksportować profilu urządzenia: {{error}}", + "device-profile-file":"Plik profilu urządzenia", + "invalid-device-profile-file-error":"Nie można zaimportować profilu urządzenia: Nieprawidłowa struktura danych profilu urządzenia.", + "power-saving-mode":"Tryb oszczędzania energii", + "power-saving-mode-type":{ + "default":"Użyj trybu oszczędzania energii profilu urządzenia", + "psm":"Tryb oszczędzania energii (PSM)", + "drx":"Przerwane odbieranie (DRX)", + "edrx":"Rozszerzone przerwane odbieranie (eDRX)" + }, + "edrx-cycle":"Cykl eDRX", + "edrx-cycle-required":"Cykl eDRX jest wymagany.", + "edrx-cycle-pattern":"Cykl eDRX musi być liczbą całkowitą dodatnią.", + "edrx-cycle-min":"Minimalna liczba cykli eDRX to {{ min }} sekundy.", + "paging-transmission-window":"Okno transmisji strony", + "paging-transmission-window-required":"Okno transmisji strony jest wymagane.", + "paging-transmission-window-pattern":"Okno transmisji strony musi być liczbą całkowitą dodatnią.", + "paging-transmission-window-min":"Minimalna liczba okna transmisji strony to {{ min }} sekundy.", + "psm-activity-timer":"Licznik aktywności PSM", + "psm-activity-timer-required":"Licznik aktywności PSM jest wymagany.", + "psm-activity-timer-pattern":"Licznik aktywności PSM musi być liczbą całkowitą dodatnią.", + "psm-activity-timer-min":"Minimalna liczba licznika aktywności PSM to {{ min }} sekundy.", + "lwm2m":{ + "object-list":"Lista obiektów", + "object-list-empty":"Brak wybranych obiektów.", + "no-objects-found":"Nie znaleziono obiektów.", + "no-objects-matching":"Nie znaleziono obiektów pasujących do '{{object}}'.", + "model-tab":"Model LWM2M", + "add-new-instances":"Dodaj nowe instancje", + "instances-list":"Lista instancji", + "instances-list-required":"Lista instancji jest wymagana.", + "instance-id-pattern":"Identyfikator instancji musi być liczbą całkowitą dodatnią.", + "instance-id-max":"Maksymalna wartość identyfikatora instancji to {{max}}.", + "instance":"Instancja", + "resource-label":"#ID Nazwa zasobu", + "observe-label":"Obserwuj", + "attribute-label":"Atrybut", + "telemetry-label":"Telemetria", + "edit-observe-select":"Aby edytować obserwację, wybierz telemetrię lub atrybut", + "edit-attributes-select":"Aby edytować atrybuty, wybierz telemetrię lub atrybut", + "no-attributes-set":"Brak ustawionych atrybutów", + "key-name":"Nazwa klucza", + "key-name-required":"Nazwa klucza jest wymagana", + "attribute-name":"Nazwa atrybutu", + "attribute-name-required":"Nazwa atrybutu jest wymagana.", + "attribute-value":"Wartość atrybutu", + "attribute-value-required":"Wartość atrybutu jest wymagana.", + "attribute-value-pattern":"Wartość atrybutu musi być liczbą całkowitą dodatnią.", + "edit-attributes":"Edytuj atrybuty: {{ name }}", + "view-attributes":"Wyświetl atrybuty: {{ name }}", + "add-attribute":"Dodaj atrybut", + "edit-attribute":"Edytuj atrybut", + "view-attribute":"Wyświetl atrybut", + "remove-attribute":"Usuń atrybut", + "delete-server-text":"Bądź ostrożny, po potwierdzeniu konfiguracja serwera stanie się nieodwracalna.", + "delete-server-title":"Czy na pewno chcesz usunąć serwer?", + "mode":"Tryb konfiguracji zabezpieczeń", + "bootstrap-tab":"Bootstrap", + "bootstrap-server-legend":"Serwer Bootstrap (ShortId...)", + "lwm2m-server-legend":"Serwer LwM2M (ShortId...)", + "server":"Serwer", + "short-id":"Krótki identyfikator serwera", + "short-id-tooltip":"Krótki identyfikator serwera. Używany jako łącze do powiązania instancji obiektu serwera.\nTen identyfikator jednoznacznie identyfikuje każdy serwer LwM2M skonfigurowany dla klienta LwM2M.\nZasób MUSI być ustawiony, gdy Zasób Bootstrap-Server ma wartość „false”.\nWartości ID:0 i ID:65535 NIE MOGĄ być używane do identyfikacji serwera LwM2M.", + "short-id-required":"Krótki identyfikator serwera jest wymagany.", + "short-id-range":"Krótki identyfikator serwera powinien mieć wartość z zakresu od 1 do 65534.", + "short-id-pattern":"Krótki identyfikator serwera musi być liczbą całkowitą dodatnią.", + "lifetime":"Czas życia rejestracji klienta", + "lifetime-required":"Czas życia rejestracji klienta jest wymagany.", + "lifetime-pattern":"Czas życia rejestracji klienta musi być liczbą całkowitą dodatnią.", + "default-min-period":"Minimalny okres między dwoma powiadomieniami (s)", + "default-min-period-tooltip":"Domyślna wartość, jaką klient LwM2M powinien używać dla Minimalnego Okresu Obserwacji w przypadku braku uwzględnienia tego parametru w Obserwacji.", + "default-min-period-required":"Minimalny okres jest wymagany.", + "default-min-period-pattern":"Minimalny okres musi być liczbą całkowitą dodatnią.", + "notification-storing":"Przechowywanie powiadomień przy wyłączonym lub offline", + "binding":"Powiązanie", + "binding-type":{ + "u":"U: Klient jest dostępny za pośrednictwem powiązania UDP w dowolnym czasie.", + "m":"M: Klient jest dostępny za pośrednictwem powiązania MQTT w dowolnym czasie.", + "h":"H: Klient jest dostępny za pośrednictwem powiązania HTTP w dowolnym czasie.", + "t":"T: Klient jest dostępny za pośrednictwem powiązania TCP w dowolnym czasie.", + "s":"S: Klient jest dostępny za pośrednictwem powiązania SMS w dowolnym czasie.", + "n":"N: Klient MUSI wysłać odpowiedź na takie żądanie za pośrednictwem powiązania Non-IP (jest obsługiwane od LWM2M 1.1).", + "uq":"UQ: Połączenie UDP w trybie kolejki (nie jest obsługiwane od LWM2M 1.1)", + "uqs":"UQS: zarówno połączenia UDP, jak i SMS aktywne; UDP w trybie kolejki, SMS w trybie standardowym (nie jest obsługiwane od LWM2M 1.1)", + "tq":"TQ: Połączenie TCP w trybie kolejki (nie jest obsługiwane od LWM2M 1.1)", + "tqs":"TQS: zarówno połączenia TCP, jak i SMS aktywne; TCP w trybie kolejki, SMS w trybie standardowym (nie jest obsługiwane od LWM2M 1.1)", + "sq":"SQ: Połączenie SMS w trybie kolejki (nie jest obsługiwane od LWM2M 1.1)" + }, + "binding-tooltip":"To jest lista w zasobie \"binding\" obiektu serwera LwM2M - /1/x/7. Wskazuje obsługiwane tryby powiązań w kliencie LwM2M. Ta wartość POWINNA być taka sama jak wartość w zasobie \"Supported Binding and Modes\" w obiekcie urządzenia (/3/0/16). Chociaż obsługiwane są różne środki transportu, tylko jedno powiązanie transportowe może być używane podczas całej sesji transportowej. Na przykład, gdy obsługiwane są zarówno UDP, jak i SMS, klient LwM2M i serwer LwM2M mogą wybrać komunikację zarówno przez UDP, jak i SMS przez całą sesję transportową.", + "bootstrap-server":"Serwer rozruchowy", + "lwm2m-server":"Serwer LwM2M", + "include-bootstrap-server":"Załącz aktualizacje serwera rozruchowego", + "bootstrap-update-title":"Masz już skonfigurowany serwer rozruchowy. Czy na pewno chcesz wyłączyć aktualizacje?", + "bootstrap-update-text":"Bądź ostrożny, po potwierdzeniu konfiguracja danych serwera rozruchowego stanie się nieodwracalna.", + "server-host":"Host", + "server-host-required":"Host jest wymagany.", + "server-port":"Port", + "server-port-required":"Port jest wymagany.", + "server-port-pattern":"Port musi być liczbą całkowitą dodatnią.", + "server-port-range":"Port powinien być w zakresie od 1 do 65535.", + "server-public-key":"Klucz publiczny serwera", + "server-public-key-required":"Klucz publiczny serwera jest wymagany.", + "client-hold-off-time":"Czas wstrzymania klienta", + "client-hold-off-time-required":"Czas wstrzymania klienta jest wymagany.", + "client-hold-off-time-pattern":"Czas wstrzymania klienta musi być liczbą całkowitą dodatnią.", + "client-hold-off-time-tooltip":"Czas wstrzymania klienta do użycia tylko z serwerem rozruchowym", + "account-after-timeout":"Konto po przekroczeniu czasu oczekiwania", + "account-after-timeout-required":"Konto po przekroczeniu czasu oczekiwania jest wymagane.", + "account-after-timeout-pattern":"Konto po przekroczeniu czasu oczekiwania musi być liczbą całkowitą dodatnią.", + "account-after-timeout-tooltip":"Wartość konta po przekroczeniu czasu oczekiwania podana przez ten zasób serwera rozruchowego.", + "server-type":"Typ serwera", + "add-new-server-title":"Dodaj nową konfigurację serwera", + "add-server-config":"Dodaj konfigurację serwera", + "add-lwm2m-server-config":"Dodaj serwer LwM2M", + "no-config-servers":"Brak skonfigurowanych serwerów", + "others-tab":"Inne ustawienia", + "client-strategy":"Strategia klienta podczas łączenia", + "client-strategy-label":"Strategia", + "client-strategy-only-observe":"Tylko żądanie obserwacji do klienta po połączeniu początkowym", + "client-strategy-read-all":"Odczytaj wszystkie zasoby i żądanie obserwacji do klienta po rejestracji", + "fw-update":"Aktualizacja oprogramowania", + "fw-update-strategy":"Strategia aktualizacji firmware'u", + "fw-update-strategy-data":"Przesuń aktualizację firmware'u jako plik binarny za pomocą Obiektu 19 i Zasobu 0 (Dane)", + "fw-update-strategy-package":"Przesuń aktualizację firmware'u jako plik binarny za pomocą Obiektu 5 i Zasobu 0 (Pakiet)", + "fw-update-strategy-package-uri":"Automatycznie generuj unikalny adres URL CoAP do pobrania pakietu i przesyłaj aktualizację firmware'u jako Obiekt 5 i Zasób 1 (Pakiet URI)", + "sw-update":"Aktualizacja oprogramowania", + "sw-update-strategy":"Strategia aktualizacji oprogramowania", + "sw-update-strategy-package":"Przesuń plik binarny za pomocą Obiektu 9 i Zasobu 2 (Pakiet)", + "sw-update-strategy-package-uri":"Automatycznie generuj unikalny adres URL CoAP do pobrania pakietu i przesyłaj aktualizację oprogramowania za pomocą Obiektu 9 i Zasób 3 (Pakiet URI)", + "fw-update-resource":"Zasób CoAP aktualizacji firmware'u", + "fw-update-resource-required":"Zasób CoAP aktualizacji firmware'u jest wymagany.", + "sw-update-resource":"Zasób CoAP aktualizacji oprogramowania", + "sw-update-resource-required":"Zasób CoAP aktualizacji oprogramowania jest wymagany.", + "config-json-tab":"Profil konfiguracji urządzenia Json", + "attributes-name":{ + "min-period":"Minimalny okres", + "max-period":"Maksymalny okres", + "greater-than":"Większe niż", + "less-than":"Mniejsze niż", + "step":"Krok", + "min-evaluation-period":"Minimalny okres oceny", + "max-evaluation-period":"Maksymalny okres oceny" + }, + "composite-operations-support":"Obsługuje operacje złożone Odczyt/Zapis/Obserwacja" + }, + "snmp":{ + "add-communication-config":"Dodaj konfigurację komunikacji", + "add-mapping":"Dodaj mapowanie", + "authentication-passphrase":"Hasło uwierzytelniania", + "authentication-passphrase-required":"Wymagane hasło uwierzytelniania.", + "authentication-protocol":"Protokół uwierzytelniania", + "authentication-protocol-required":"Wymagany protokół uwierzytelniania.", + "communication-configs":"Konfiguracje komunikacji", + "community":"Ciąg społeczności", + "community-required":"Wymagany ciąg społeczności.", + "context-name":"Nazwa kontekstu", + "data-key":"Klucz danych", + "data-key-required":"Wymagany klucz danych.", + "data-type":"Typ danych", + "data-type-required":"Wymagany typ danych.", + "engine-id":"ID silnika", + "host":"Host", + "host-required":"Wymagany host.", + "oid":"OID", + "oid-pattern":"Nieprawidłowy format OID.", + "oid-required":"Wymagany OID.", + "please-add-communication-config":"Proszę dodać konfigurację komunikacji", + "please-add-mapping-config":"Proszę dodać konfigurację mapowania", + "port":"Port", + "port-format":"Nieprawidłowy format portu.", + "port-required":"Wymagany port.", + "privacy-passphrase":"Hasło prywatności", + "privacy-passphrase-required":"Wymagane hasło prywatności.", + "privacy-protocol":"Protokół prywatności", + "privacy-protocol-required":"Wymagany protokół prywatności.", + "protocol-version":"Wersja protokołu", + "protocol-version-required":"Wymagana wersja protokołu.", + "querying-frequency":"Częstotliwość zapytań, ms", + "querying-frequency-invalid-format":"Częstotliwość zapytań musi być liczbą całkowitą dodatnią.", + "querying-frequency-required":"Wymagana częstotliwość zapytań.", + "retries":"Ponowienia", + "retries-invalid-format":"Liczba ponowień musi być liczbą całkowitą dodatnią.", + "retries-required":"Wymagane ponowienia.", + "scope":"Zakres", + "scope-required":"Wymagany zakres.", + "security-name":"Nazwa zabezpieczeń", + "security-name-required":"Wymagana nazwa zabezpieczeń.", + "timeout-ms":"Limit czasu, ms", + "timeout-ms-invalid-format":"Limit czasu musi być liczbą całkowitą dodatnią.", + "timeout-ms-required":"Wymagany limit czasu.", + "user-name":"Nazwa użytkownika", + "user-name-required":"Wymagana nazwa użytkownika." + } + }, + "dialog":{ + "close":"Zamknij okno dialogowe", + "error-message-title":"Wiadomość o błędzie:", + "error-details-title":"Szczegóły błędu" + }, + "direction":{ + "column":"Kolumna", + "row":"Wiersz" + }, + "edge":{ + "edge":"Krawędź", + "edge-instances":"Instancje krawędzi", + "instances":"Instancje", + "edge-file":"Plik krawędzi", + "name-max-length":"Nazwa powinna być krótsza niż 256", + "label-max-length":"Etykieta powinna być krótsza niż 256", + "type-max-length":"Typ powinien być krótszy niż 256", + "management":"Zarządzanie krawędzią", + "no-edges-matching":"Brak krawędzi pasujących do '{{entity}}'", + "add":"Dodaj krawędź", + "no-edges-text":"Brak znalezionych krawędzi", + "edge-details":"Szczegóły krawędzi", + "add-edge-text":"Dodaj nową krawędź", + "delete":"Usuń krawędź", + "delete-edge-title":"Czy na pewno chcesz usunąć krawędź '{{edgeName}}'?", + "delete-edge-text":"Bądź ostrożny, po potwierdzeniu krawędź i wszystkie związane z nią dane staną się nieodwracalnie utracone.", + "delete-edges-title":"Czy na pewno chcesz usunąć krawędź { count, plural, =1 {1 krawędź} other {# krawędzi} }?", + "delete-edges-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane krawędzie zostaną usunięte, a wszystkie związane z nimi dane staną się nieodwracalnie utracone.", + "name":"Nazwa", + "name-starts-with":"Nazwa krawędzi zaczyna się od", + "name-required":"Nazwa jest wymagana.", + "description":"Opis", + "details":"Szczegóły", + "events":"Zdarzenia", + "copy-id":"Kopiuj identyfikator krawędzi", + "id-copied-message":"Identyfikator krawędzi został skopiowany do schowka", + "sync":"Synchronizuj krawędź", + "edge-required":"Wymagana krawędź", + "edge-type":"Typ krawędzi", + "edge-type-required":"Wymagany jest typ krawędzi.", + "event-action":"Akcja zdarzenia", + "entity-id":"Identyfikator jednostki", + "select-edge-type":"Wybierz typ krawędzi", + "assign-to-customer":"Przypisz do klienta", + "assign-to-customer-text":"Proszę wybrać klienta, do którego przypisane będą krawędzie", + "assign-edge-to-customer":"Przypisz krawędź do klienta", + "assign-edge-to-customer-text":"Proszę wybrać krawędzie do przypisania do klienta", + "assignedToCustomer":"Przypisane do klienta", + "edge-public":"Krawędź jest publiczna", + "assigned-to-customer":"Przypisane do: {{customerTitle}}", + "unassign-from-customer":"Odłącz od klienta", + "unassign-edge-title":"Czy na pewno chcesz odłączyć krawędź '{{edgeName}}'?", + "unassign-edge-text":"Po potwierdzeniu krawędź zostanie odłączona i nie będzie dostępna dla klienta.", + "unassign-edges-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 krawędź} other {# krawędzie} }?", + "unassign-edges-text":"Po potwierdzeniu wszystkie wybrane krawędzie zostaną odłączone i nie będą dostępne dla klienta.", + "make-public":"Udostępnij publicznie", + "make-public-edge-title":"Czy na pewno chcesz udostępnić publicznie krawędź '{{edgeName}}'?", + "make-public-edge-text":"Po potwierdzeniu krawędź i wszystkie jej dane zostaną udostępnione publicznie i będą dostępne dla innych.", + "make-private":"Udostępnij prywatnie", + "public":"Publiczne", + "make-private-edge-title":"Czy na pewno chcesz udostępnić prywatnie krawędź '{{edgeName}}'?", + "make-private-edge-text":"Po potwierdzeniu krawędź i wszystkie jej dane zostaną udostępnione prywatnie i nie będą dostępne dla innych.", + "import":"Importuj krawędź", + "install-connect-instructions":"Instrukcje instalacji i połączenia", + "install-connect-instructions-edge-created":"Krawędź utworzona! Sprawdź instrukcje instalacji i połączenia", + "loading-edge-instructions":"Ładowanie instrukcji krawędzi...", + "label":"Etykieta", + "load-entity-error":"Nie udało się załadować danych. Jednostka została usunięta.", + "assign-new-edge":"Przypisz nową krawędź", + "unassign-from-edge":"Odłącz od krawędzi", + "edge-key":"Klucz krawędzi", + "copy-edge-key":"Kopiuj klucz krawędzi", + "edge-key-copied-message":"Klucz krawędzi został skopiowany do schowka", + "edge-secret":"Sekret krawędzi", + "copy-edge-secret":"Kopiuj sekret krawędzi", + "edge-secret-copied-message":"Sekret krawędzi został skopiowany do schowka", + "manage-assets":"Zarządzaj zasobami", + "manage-devices":"Zarządzaj urządzeniami", + "manage-entity-views":"Zarządzaj widokami jednostek", + "manage-dashboards":"Zarządzaj pulpitem nawigacyjnym", + "manage-rulechains":"Zarządzaj łańcuchami reguł", + "assets":"Zasoby krawędzi", + "devices":"Urządzenia krawędziowe", + "entity-views":"Widoki jednostek krawędziowych", + "dashboard":"Pulpit nawigacyjny krawędzi", + "dashboards":"Panele nawigacyjne krawędziowe", + "rulechain-templates":"Szablony łańcucha reguł", + "edge-rulechain-templates":"Szablony łańcucha reguł krawędziowych", + "rulechains":"Łańcuchy reguł krawędziowych", + "search":"Wyszukaj krawędzie", + "selected-edges":"{ count, plural, =1 {1 krawędź} other {# krawędzie} } wybrana", + "any-edge":"Dowolna krawędź", + "no-edge-types-matching":"Brak typów krawędzi pasujących do '{{entitySubtype}}'", + "edge-type-list-empty":"Brak wybranych typów krawędzi.", + "edge-types":"Typy krawędzi", + "enter-edge-type":"Wprowadź typ krawędzi", + "deployed":"Wdrożone", + "pending":"Oczekujące", + "downlinks":"Downlinki", + "no-downlinks-prompt":"Brak znalezionych downlinków", + "sync-process-started-successfully":"Proces synchronizacji został pomyślnie uruchomiony!", + "missing-related-rule-chains-title":"Krawędź ma brakujące powiązane łańcuchy reguł", + "missing-related-rule-chains-text":"Przypisane do krawędzi łańcuchy reguł używają węzłów reguł, które przekazują wiadomości do łańcuchów reguł, które nie są przypisane do tej krawędzi.

Lista brakujących łańcuchów reguł:
{{missingRuleChains}}", + "upgrade-instructions":"Instrukcje aktualizacji", + "widget-datasource-error":"Ten widżet obsługuje tylko źródło danych krawędziowe" + }, + "edge-event":{ + "type-dashboard":"Panel nawigacyjny", + "type-asset":"Zasób", + "type-device":"Urządzenie", + "type-device-profile":"Profil urządzenia", + "type-asset-profile":"Profil zasobu", + "type-entity-view":"Widok jednostki", + "type-alarm":"Alarm", + "type-rule-chain":"Łańcuch reguł", + "type-rule-chain-metadata":"Metadane łańcucha reguł", + "type-edge":"Krawędź", + "type-user":"Użytkownik", + "type-tenant":"Najemca", + "type-tenant-profile":"Profil najemcy", + "type-customer":"Klient", + "type-relation":"Relacja", + "type-widgets-bundle":"Pakiet widżetów", + "type-widgets-type":"Typ widżetu", + "type-admin-settings":"Ustawienia administratora", + "type-ota-package":"Pakiet OTA", + "type-queue":"Kolejka", + "action-type-added":"Dodane", + "action-type-deleted":"Usunięte", + "action-type-updated":"Zaktualizowane", + "action-type-post-attributes":"Opublikuj atrybuty", + "action-type-attributes-updated":"Zaktualizowane atrybuty", + "action-type-attributes-deleted":"Usunięte atrybuty", + "action-type-timeseries-updated":"Zaktualizowane serie czasowe", + "action-type-credentials-updated":"Zaktualizowane dane uwierzytelniające", + "action-type-assigned-to-customer":"Przypisane do klienta", + "action-type-unassigned-from-customer":"Odłączone od klienta", + "action-type-relation-add-or-update":"Dodaj lub aktualizuj relację", + "action-type-relation-deleted":"Usunięta relacja", + "action-type-rpc-call":"Wywołanie RPC", + "action-type-alarm-ack":"Potwierdzenie alarmu", + "action-type-alarm-clear":"Wyczyść alarm", + "action-type-alarm-assigned":"Przypisany alarm", + "action-type-alarm-unassigned":"Odłączony alarm", + "action-type-assigned-to-edge":"Przypisane do krawędzi", + "action-type-unassigned-from-edge":"Odłączone od krawędzi", + "action-type-credentials-request":"Żądanie danych uwierzytelniających", + "action-type-entity-merge-request":"Żądanie scalenia jednostki" + }, + "error":{ + "unable-to-connect":"Nie można połączyć się z serwerem! Sprawdź swoje połączenie internetowe.", + "unhandled-error-code":"Niestandardowy kod błędu: {{errorCode}}", + "unknown-error":"Nieznany błąd" + }, + "entity":{ + "entity":"Encja", + "entities":"Encje", + "entities-count":"Liczba encji", + "alarms-count":"Liczba alarmów", + "aliases":"Aliasy encji", + "aliases-short":"Aliasy", + "entity-alias":"Alias encji", + "unable-delete-entity-alias-title":"Nie można usunąć aliasu encji", + "unable-delete-entity-alias-text":"Alias encji '{{entityAlias}}' nie może zostać usunięty, ponieważ jest używany przez następujące widżety:
{{widgetsList}}", + "duplicate-alias-error":"Znaleziono zduplikowany alias '{{alias}}'.
Aleasy encji muszą być unikalne w obrębie deski rozdzielczej.", + "missing-entity-filter-error":"Brakuje filtru dla aliasu '{{alias}}'.", + "configure-alias":"Skonfiguruj alias '{{alias}}'", + "alias":"Alias", + "alias-required":"Wymagany jest alias encji.", + "remove-alias":"Usuń alias encji", + "add-alias":"Dodaj alias encji", + "entity-list":"Lista encji", + "entity-type":"Typ encji", + "entity-types":"Typy encji", + "entity-type-list":"Lista typów encji", + "any-entity":"Dowolna encja", + "add-entity-type":"Dodaj typ encji", + "enter-entity-type":"Wprowadź typ encji", + "no-entities-matching":"Nie znaleziono encji pasujących do '{{entity}}'.", + "no-entity-types-matching":"Nie znaleziono typów encji pasujących do '{{entityType}}'.", + "name-starts-with":"Wyrażenie nazwy", + "help-text":"Użyj '%', zgodnie z potrzebą: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter":"Użyj filtra", + "entity-list-empty":"Nie wybrano żadnych encji.", + "entity-type-list-required":"Należy wybrać co najmniej jeden typ encji.", + "entity-name-filter-required":"Wymagany jest filtr nazwy encji.", + "entity-name-filter-no-entity-matched":"Nie znaleziono encji rozpoczynających się od '{{entity}}'.", + "all-subtypes":"Wszystkie", + "select-entities":"Wybierz encje", + "no-aliases-found":"Nie znaleziono aliasów.", + "no-alias-matching":"'{{alias}}' nie znaleziono.", + "create-new-alias":"Utwórz nowy!", + "create-new":"Utwórz nowy", + "key":"Klucz", + "key-name":"Nazwa klucza", + "no-keys-found":"Nie znaleziono kluczy.", + "no-key-matching":"'{{key}}' nie znaleziono.", + "create-new-key":"Utwórz nowy!", + "type":"Typ", + "type-required":"Wymagany jest typ encji.", + "type-device":"Urządzenie", + "type-devices":"Urządzenia", + "list-of-devices":"{ count, plural, =1 {Jedno urządzenie} other {Lista # urządzeń} }", + "device-name-starts-with":"Urządzenia, których nazwy zaczynają się od '{{prefix}}'", + "type-device-profile":"Profil urządzenia", + "type-device-profiles":"Profile urządzeń", + "clear-selected-profiles":"Wyczyść wybrane profile", + "list-of-device-profiles":"{ count, plural, =1 {Jeden profil urządzenia} other {Lista # profili urządzeń} }", + "device-profile-name-starts-with":"Profile urządzeń, których nazwy zaczynają się od '{{prefix}}'", + "type-asset-profile":"Profil zasobów", + "type-asset-profiles":"Profile zasobów", + "list-of-asset-profiles":"{ count, plural, =1 {Jeden profil zasobów} other {Lista # profili zasobów} }", + "asset-profile-name-starts-with":"Profile zasobów, których nazwy zaczynają się od '{{prefix}}'", + "type-asset":"Zasób", + "type-assets":"Zasoby", + "list-of-assets":"{ count, plural, =1 {Jeden zasób} other {Lista # zasobów} }", + "asset-name-starts-with":"Zasoby, których nazwy zaczynają się od '{{prefix}}'", + "type-entity-view":"Widok encji", + "type-entity-views":"Widoki encji", + "list-of-entity-views":"{ count, plural, =1 {Jeden widok encji} other {Lista # widoków encji} }", + "entity-view-name-starts-with":"Widoki encji, których nazwy zaczynają się od '{{prefix}}'", + "type-rule":"Reguła", + "type-rules":"Reguły", + "list-of-rules":"{ count, plural, =1 {Jedna reguła} other {Lista # reguł} }", + "rule-name-starts-with":"Reguły, których nazwy zaczynają się od '{{prefix}}'", + "type-plugin":"Wtyczka", + "type-plugins":"Wtyczki", + "list-of-plugins":"{ count, plural, =1 {Jedna wtyczka} other {Lista # wtyczek} }", + "plugin-name-starts-with":"Wtyczki, których nazwy zaczynają się od '{{prefix}}'", + "type-tenant":"Najemca", + "type-tenants":"Najemcy", + "list-of-tenants":"{ count, plural, =1 {Jeden najemca} other {Lista # najemców} }", + "tenant-name-starts-with":"Najemcy, których nazwy zaczynają się od '{{prefix}}'", + "type-tenant-profile":"Profil najemcy", + "type-tenant-profiles":"Profile najemcy", + "list-of-tenant-profiles":"{ count, plural, =1 {Jeden profil najemcy} other {Lista # profili najemcy} }", + "tenant-profile-name-starts-with":"Profile najemcy, których nazwy zaczynają się od '{{prefix}}'", + "type-customer":"Klient", + "type-customers":"Klienci", + "list-of-customers":"{ count, plural, =1 {Jeden klient} other {Lista # klientów} }", + "customer-name-starts-with":"Klienci, których nazwy zaczynają się od '{{prefix}}'", + "type-user":"Użytkownik", + "type-users":"Użytkownicy", + "list-of-users":"{ count, plural, =1 {Jeden użytkownik} other {Lista # użytkowników} }", + "user-name-starts-with":"Użytkownicy, których nazwy zaczynają się od '{{prefix}}'", + "type-dashboard":"Deska rozdzielcza", + "type-dashboards":"Deski rozdzielcze", + "list-of-dashboards":"{ count, plural, =1 {Jedna deska rozdzielcza} other {Lista # desek rozdzielczych} }", + "dashboard-name-starts-with":"Deski rozdzielcze, których nazwy zaczynają się od '{{prefix}}'", + "type-alarm":"Alarm", + "type-alarms":"Alarmy", + "list-of-alarms":"{ count, plural, =1 {Jeden alarm} other {Lista # alarmów} }", + "alarm-name-starts-with":"Alarmy, których nazwy zaczynają się od '{{prefix}}'", + "type-rulechain":"Łańcuch reguł", + "type-rulechains":"Łańcuchy reguł", + "list-of-rulechains":"{ count, plural, =1 {Jeden łańcuch reguł} other {Lista # łańcuchów reguł} }", + "rulechain-name-starts-with":"Łańcuchy reguł, których nazwy zaczynają się od '{{prefix}}'", + "type-rulenode":"Węzeł reguły", + "type-rulenodes":"Węzły reguł", + "list-of-rulenodes":"{ count, plural, =1 {Jeden węzeł reguły} other {Lista # węzłów reguł} }", + "rulenode-name-starts-with":"Węzły reguł, których nazwy zaczynają się od '{{prefix}}'", + "type-current-customer":"Bieżący Klient", + "type-current-tenant":"Bieżący Najemca", + "type-current-user":"Bieżący Użytkownik", + "type-current-user-owner":"Bieżący Właściciel Użytkownika", + "type-widgets-bundle":"Zestaw Widżetów", + "type-widgets-bundles":"Zestawy Widżetów", + "list-of-widgets-bundles":"{ count, plural, =1 {Jeden zestaw widżetów} other {Lista # zestawów widżetów} }", + "type-widget":"Widżet", + "type-widgets":"Widżety", + "list-of-widgets":"{ count, plural, =1 {Jeden widżet} other {Lista # widżetów} }", + "search":"Szukaj jednostek", + "selected-entities":"{ count, plural, =1 {1 jednostka} other {# jednostki} } wybranych", + "entity-name":"Nazwa jednostki", + "entity-label":"Etykieta jednostki", + "details":"Szczegóły jednostki", + "no-entities-prompt":"Nie znaleziono jednostek", + "no-data":"Brak danych do wyświetlenia", + "columns-to-display":"Kolumny do wyświetlenia", + "type-api-usage-state":"Stan korzystania z API", + "type-edge":"Edge", + "type-edges":"Edges", + "list-of-edges":"{ count, plural, =1 {Jeden edge} other {Lista # edges} }", + "edge-name-starts-with":"Edges, których nazwy zaczynają się od '{{prefix}}'", + "type-tb-resource":"Zasób", + "type-tb-resources":"Zasoby", + "list-of-tb-resources":"{ count, plural, =1 {Jeden zasób} other {Lista # zasobów} }", + "type-ota-package":"Pakiet OTA", + "type-rpc":"RPC", + "type-queue":"Kolejka", + "type-notification":"Powiadomienie", + "type-notification-rule":"Reguła powiadomień", + "type-notification-rules":"Reguły powiadomień", + "list-of-notification-rules":"{ count, plural, =1 {Jedna reguła powiadomień} other {Lista # reguł powiadomień} }", + "type-notification-target":"Odbiorca powiadomienia", + "type-notification-targets":"Odbiorcy powiadomień", + "list-of-notification-targets":"{ count, plural, =1 {Jeden odbiorca powiadomienia} other {Lista # odbiorców powiadomień} }", + "type-notification-request":"Żądanie powiadomienia", + "type-notification-template":"Szablon powiadomienia", + "type-notification-templates":"Szablony powiadomień", + "list-of-notification-templates":"{ count, plural, =1 {Jeden szablon powiadomienia} other {Lista # szablonów powiadomień} }" + }, + "entity-field":{ + "created-time":"Czas utworzenia", + "name":"Nazwa", + "type":"Typ", + "first-name":"Imię", + "last-name":"Nazwisko", + "email":"E-mail", + "title":"Tytuł", + "country":"Kraj", + "state":"Stan", + "city":"Miasto", + "address":"Adres", + "address2":"Adres 2", + "zip":"Kod pocztowy", + "phone":"Telefon", + "label":"Etykieta" + }, + "entity-view":{ + "entity-view":"Widok encji", + "entity-view-required":"Widok encji jest wymagany.", + "entity-views":"Widoki encji", + "management":"Zarządzanie widokiem encji", + "view-entity-views":"Wyświetl widoki encji", + "entity-view-alias":"Alias widoku encji", + "aliases":"Aliasy widoku encji", + "no-alias-matching":"'{{alias}}' nie znaleziono.", + "no-aliases-found":"Nie znaleziono aliasów.", + "no-key-matching":"'{{key}}' nie znaleziono.", + "no-keys-found":"Nie znaleziono kluczy.", + "create-new-alias":"Utwórz nowy!", + "create-new-key":"Utwórz nowy!", + "duplicate-alias-error":"Znaleziono zduplikowany alias '{{alias}}'.
Aliasy widoku encji muszą być unikalne w obrębie panelu.", + "configure-alias":"Konfiguruj alias '{{alias}}'", + "no-entity-views-matching":"Brak widoków encji pasujących do '{{entity}}'.", + "public":"Publiczny", + "alias":"Alias", + "alias-required":"Wymagany jest alias widoku encji.", + "remove-alias":"Usuń alias widoku encji", + "add-alias":"Dodaj alias widoku encji", + "name-starts-with":"Wyrażenie nazwy widoku encji", + "help-text":"Użyj '%' według potrzeb: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list":"Lista widoków encji", + "use-entity-view-name-filter":"Użyj filtra", + "entity-view-list-empty":"Brak wybranych widoków encji.", + "entity-view-name-filter-required":"Wymagany jest filtr nazwy widoku encji.", + "entity-view-name-filter-no-entity-view-matched":"Nie znaleziono widoków encji rozpoczynających się od '{{entityView}}'.", + "add":"Dodaj widok encji", + "entity-view-public":"Widok encji jest publiczny", + "assign-to-customer":"Przypisz do klienta", + "assign-entity-view-to-customer":"Przypisz Widoki Encji Do Klienta", + "assign-entity-view-to-customer-text":"Wybierz widoki encji do przypisania do klienta", + "assign-entity-view-to-edge-title":"Przypisz Widoki Encji Do Krawędzi", + "no-entity-views-text":"Nie znaleziono widoków encji", + "assign-to-customer-text":"Wybierz klienta, do którego chcesz przypisać widoki encji", + "entity-view-details":"Szczegóły widoku encji", + "add-entity-view-text":"Dodaj nowy widok encji", + "delete":"Usuń widok encji", + "assign-entity-views":"Przypisz widoki encji", + "assign-entity-views-text":"Przypisz { count, plural, =1 {1 widok encji} other {# widoków encji} } do klienta", + "delete-entity-views":"Usuń widoki encji", + "unassign-from-customer":"Odłącz od klienta", + "unassign-entity-views":"Odłącz widoki encji", + "unassign-entity-views-action-title":"Odłącz { count, plural, =1 {1 widok encji} other {# widoków encji} } od klienta", + "assign-new-entity-view":"Przypisz nowy widok encji", + "delete-entity-view-title":"Czy na pewno chcesz usunąć widok encji '{{entityViewName}}'?", + "delete-entity-view-text":"Bądź ostrożny, po potwierdzeniu widok encji i wszystkie związane z nim dane staną się nieodwracalnie utracone.", + "delete-entity-views-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 widok encji} other {# widoków encji} }?", + "delete-entity-views-action-title":"Usuń { count, plural, =1 {1 widok encji} other {# widoków encji} }", + "delete-entity-views-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane widoki encji zostaną usunięte, a wszystkie związane z nimi dane staną się nieodwracalnie utracone.", + "unassign-entity-view-title":"Czy na pewno chcesz odłączyć widok encji '{{entityViewName}}'?", + "unassign-entity-view-text":"Po potwierdzeniu widok encji zostanie odłączony i nie będzie dostępny dla klienta.", + "unassign-entity-view":"Odłącz widok encji", + "unassign-entity-views-title":"Czy na pewno chcesz odłączyć { count, plural, =1 {1 widok encji} other {# widoków encji} }?", + "unassign-entity-views-text":"Po potwierdzeniu wszystkie wybrane widoki encji zostaną odłączone i nie będą dostępne dla klienta.", + "entity-view-type":"Typ widoku encji", + "entity-view-type-required":"Wymagany jest typ widoku encji.", + "select-entity-view-type":"Wybierz typ widoku encji", + "enter-entity-view-type":"Wprowadź typ widoku encji", + "any-entity-view":"Dowolny widok encji", + "no-entity-view-types-matching":"Brak typów widoków encji pasujących do '{{entitySubtype}}'.", + "entity-view-type-list-empty":"Brak wybranych typów widoków encji.", + "entity-view-types":"Typy widoków encji", + "created-time":"Czas utworzenia", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "name-max-length":"Nazwa powinna mieć mniej niż 256 znaków.", + "type-max-length":"Typ widoku jednostki powinien mieć mniej niż 256 znaków.", + "description":"Opis", + "events":"Zdarzenia", + "details":"Szczegóły", + "copyId":"Kopiuj identyfikator widoku jednostki", + "idCopiedMessage":"Identyfikator widoku jednostki został skopiowany do schowka.", + "assignedToCustomer":"Przypisane do klienta", + "unable-entity-view-device-alias-title":"Nie można usunąć aliasu widoku jednostki", + "unable-entity-view-device-alias-text":"Alias urządzenia '{{entityViewAlias}}' nie może zostać usunięty, ponieważ jest używany przez następujące widgety:
{{widgetsList}}", + "select-entity-view":"Wybierz widok jednostki", + "make-public":"Udostępnij widok jednostki publicznie", + "make-private":"Udostępnij widok jednostki prywatnie", + "start-date":"Data rozpoczęcia", + "start-ts":"Czas rozpoczęcia", + "end-date":"Data zakończenia", + "end-ts":"Czas zakończenia", + "date-limits":"Ograniczenia dat", + "client-attributes":"Atrybuty klienta", + "shared-attributes":"Współdzielone atrybuty", + "server-attributes":"Atrybuty serwera", + "timeseries":"Szereg czasowy", + "client-attributes-placeholder":"Atrybuty klienta", + "shared-attributes-placeholder":"Współdzielone atrybuty", + "server-attributes-placeholder":"Atrybuty serwera", + "timeseries-placeholder":"Szereg czasowy", + "target-entity":"Docelowa jednostka", + "attributes-propagation":"Propagacja atrybutów", + "attributes-propagation-hint":"Widok jednostki będzie automatycznie kopiować określone atrybuty z Docelowej Jednostki za każdym razem, gdy zapisujesz lub aktualizujesz ten widok jednostki. Ze względów wydajnościowych atrybuty Docelowej Jednostki nie są propagowane do widoku jednostki przy każdej zmianie atrybutu. Możesz włączyć automatyczną propagację, konfigurując węzeł reguły „kopiuj do widoku” w swoim łańcuchu reguł i łącząc wiadomości „Post attributes” i „Attributes Updated” z nowym węzłem reguły.", + "timeseries-data":"Dane szeregu czasowego", + "timeseries-data-hint":"Skonfiguruj klucze danych szeregu czasowego Docelowej Jednostki, które będą dostępne w widoku jednostki. Te dane szeregu czasowego są tylko do odczytu.", + "search":"Szukaj widoków jednostki", + "selected-entity-views":"{ count, plural, =1 {1 widok jednostki} other {# widoki jednostki} } wybrano", + "make-public-entity-view-title":"Czy na pewno chcesz udostępnić widok jednostki '{{entityViewName}}' publicznie?", + "make-public-entity-view-text":"Po potwierdzeniu widok jednostki i wszystkie jego dane staną się publiczne i dostępne dla innych.", + "make-private-entity-view-title":"Czy na pewno chcesz uczynić widok jednostki '{{entityViewName}}' prywatnym?", + "make-private-entity-view-text":"Po potwierdzeniu widok jednostki i wszystkie jego dane staną się prywatne i nie będą dostępne dla innych.", + "assign-entity-view-to-edge":"Przypisz widok jednostki do urządzenia", + "assign-entity-view-to-edge-text":"Wybierz widoki jednostki do przypisania do urządzenia", + "unassign-entity-view-from-edge-title":"Czy na pewno chcesz odpiąć widok jednostki '{{entityViewName}}'?", + "unassign-entity-view-from-edge-text":"Po potwierdzeniu widok jednostki zostanie odpięty i nie będzie dostępny dla urządzenia.", + "unassign-entity-views-from-edge-action-title":"Odpiąć { count, plural, =1 {1 widok jednostki} other {# widoki jednostki} } od urządzenia", + "unassign-entity-view-from-edge":"Odpiąć widok jednostki", + "unassign-entity-views-from-edge-title":"Czy na pewno chcesz odpiąć { count, plural, =1 {1 widok jednostki} other {# widoki jednostki} }?", + "unassign-entity-views-from-edge-text":"Po potwierdzeniu wszystkie wybrane widoki jednostki zostaną odpięte i nie będą dostępne dla urządzenia." + }, + "event":{ + "event-type":"Typ zdarzenia", + "events-filter":"Filtr zdarzeń", + "clean-events":"Wyczyść zdarzenia", + "type-error":"Błąd", + "type-lc-event":"Zdarzenie cyklu życia", + "type-stats":"Statystyki", + "type-debug-rule-node":"Debug", + "type-debug-rule-chain":"Debug", + "no-events-prompt":"Nie znaleziono zdarzeń", + "error":"Błąd", + "alarm":"Alarm", + "event-time":"Czas zdarzenia", + "server":"Serwer", + "body":"Treść", + "method":"Metoda", + "type":"Typ", + "message":"Wiadomość", + "message-id":"Identyfikator wiadomości", + "copy-message-id":"Kopiuj identyfikator wiadomości", + "message-type":"Typ wiadomości", + "data-type":"Typ danych", + "relation-type":"Typ relacji", + "metadata":"Metadane", + "data":"Dane", + "event":"Zdarzenie", + "status":"Status", + "success":"Sukces", + "failed":"Niepowodzenie", + "messages-processed":"Wiadomości przetworzone", + "max-messages-processed":"Maksymalna ilość wiadomości przetworzonych", + "min-messages-processed":"Minimalna ilość wiadomości przetworzonych", + "errors-occurred":"Wystąpiły błędy", + "max-errors-occurred":"Maksymalna ilość wystąpiłych błędów", + "min-errors-occurred":"Minimalna ilość wystąpiłych błędów", + "min-value":"Minimalna wartość to 0.", + "all-events":"Wszystkie", + "has-error":"Zawiera błąd", + "entity-id":"Identyfikator jednostki", + "copy-entity-id":"Kopiuj identyfikator jednostki", + "entity-type":"Typ jednostki", + "clear-filter":"Wyczyść filtr", + "clear-request-title":"Wyczyść wszystkie zdarzenia", + "clear-request-text":"Czy na pewno chcesz wyczyścić wszystkie zdarzenia?", + "started":"Rozpoczęte", + "updated":"Zaktualizowane", + "stopped":"Zatrzymane" + }, + "extension":{ + "extensions":"Rozszerzenia", + "selected-extensions":"{ count, plural, =1 {1 rozszerzenie} other {# rozszerzeń} } wybranych", + "type":"Typ", + "key":"Klucz", + "value":"Wartość", + "id":"Identyfikator", + "extension-id":"Identyfikator rozszerzenia", + "extension-type":"Typ rozszerzenia", + "transformer-json":"JSON *", + "unique-id-required":"Identyfikator rozszerzenia już istnieje.", + "delete":"Usuń rozszerzenie", + "add":"Dodaj rozszerzenie", + "edit":"Edytuj rozszerzenie", + "delete-extension-title":"Czy na pewno chcesz usunąć rozszerzenie '{{extensionId}}'?", + "delete-extension-text":"Bądź ostrożny, po potwierdzeniu rozszerzenie i wszystkie związane z nim dane staną się nieodwracalnie utracone.", + "delete-extensions-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 rozszerzenie} other {# rozszerzeń} }?", + "delete-extensions-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane rozszerzenia zostaną usunięte.", + "converters":"Konwertery", + "converter-id":"Identyfikator konwertera", + "configuration":"Konfiguracja", + "converter-configurations":"Konfiguracje konwertera", + "token":"Token bezpieczeństwa", + "add-converter":"Dodaj konwerter", + "add-config":"Dodaj konfigurację konwertera", + "device-name-expression":"Wyrażenie nazwy urządzenia", + "device-type-expression":"Wyrażenie typu urządzenia", + "custom":"Niestandardowe", + "to-double":"Do Double", + "transformer":"Transformator", + "json-required":"Wymagany jest JSON transformatora.", + "json-parse":"Nie można sparsować JSON transformatora.", + "attributes":"Atrybuty", + "add-attribute":"Dodaj atrybut", + "add-map":"Dodaj element mapowania", + "timeseries":"Szereg czasowy", + "add-timeseries":"Dodaj szereg czasowy", + "field-required":"Pole jest wymagane", + "brokers":"Brokerzy", + "add-broker":"Dodaj brokera", + "host":"Host", + "port":"Port", + "port-range":"Port powinien mieć zakres od 1 do 65535.", + "ssl":"SSL", + "credentials":"Dane uwierzytelniające", + "username":"Nazwa użytkownika", + "password":"Hasło", + "retry-interval":"Interwał ponawiania w milisekundach", + "anonymous":"Anonimowy", + "basic":"Podstawowy", + "pem":"PEM", + "ca-cert":"Plik certyfikatu CA *", + "private-key":"Plik klucza prywatnego *", + "cert":"Plik certyfikatu *", + "no-file":"Nie wybrano pliku.", + "drop-file":"Upuść plik lub kliknij, aby wybrać plik do przesłania.", + "mapping":"Mapowanie", + "topic-filter":"Filtr tematów", + "converter-type":"Typ konwertera", + "converter-json":"Json", + "json-name-expression":"Wyrażenie nazwy urządzenia JSON", + "topic-name-expression":"Wyrażenie nazwy tematu urządzenia", + "json-type-expression":"Wyrażenie typu urządzenia JSON", + "topic-type-expression":"Wyrażenie typu tematu urządzenia", + "attribute-key-expression":"Wyrażenie klucza atrybutu", + "attr-json-key-expression":"Wyrażenie klucza JSON atrybutu", + "attr-topic-key-expression":"Wyrażenie klucza tematu atrybutu", + "request-id-expression":"Wyrażenie identyfikatora żądania", + "request-id-json-expression":"Wyrażenie identyfikatora JSON żądania", + "request-id-topic-expression":"Wyrażenie identyfikatora tematu żądania", + "response-topic-expression":"Wyrażenie tematu odpowiedzi", + "value-expression":"Wyrażenie wartości", + "topic":"Temat", + "timeout":"Timeout w milisekundach", + "converter-json-required":"Wymagany jest JSON konwertera.", + "converter-json-parse":"Nie można sparsować JSON konwertera.", + "filter-expression":"Wyrażenie filtru", + "connect-requests":"Żądania połączenia", + "add-connect-request":"Dodaj żądanie połączenia", + "disconnect-requests":"Żądania rozłączenia", + "add-disconnect-request":"Dodaj żądanie rozłączenia", + "attribute-requests":"Żądania atrybutów", + "add-attribute-request":"Dodaj żądanie atrybutu", + "attribute-updates":"Aktualizacje atrybutów", + "add-attribute-update":"Dodaj aktualizację atrybutu", + "server-side-rpc":"RPC po stronie serwera", + "add-server-side-rpc-request":"Dodaj żądanie RPC po stronie serwera", + "device-name-filter":"Filtr nazwy urządzenia", + "attribute-filter":"Filtr atrybutu", + "method-filter":"Filtr metody", + "request-topic-expression":"Wyrażenie tematu żądania", + "response-timeout":"Timeout odpowiedzi w milisekundach", + "topic-expression":"Wyrażenie tematu", + "client-scope":"Zakres klienta", + "add-device":"Dodaj urządzenie", + "opc-server":"Serwery OPC", + "opc-add-server":"Dodaj serwer OPC", + "opc-add-server-prompt":"Proszę dodać serwer OPC", + "opc-application-name":"Nazwa aplikacji", + "opc-application-uri":"URI aplikacji", + "opc-scan-period-in-seconds":"Okres skanowania w sekundach", + "opc-security":"Bezpieczeństwo", + "opc-identity":"Tożsamość", + "opc-keystore":"Schronisko klucza", + "opc-type":"Typ", + "opc-keystore-type":"Typ schroniska klucza", + "opc-keystore-location":"Lokalizacja *", + "opc-keystore-password":"Hasło", + "opc-keystore-alias":"Alias", + "opc-keystore-key-password":"Hasło klucza", + "opc-device-node-pattern":"Wzorzec węzła urządzenia", + "opc-device-name-pattern":"Wzorzec nazwy urządzenia", + "modbus-server":"Serwery/slave'y Modbus", + "modbus-add-server":"Dodaj serwer/slave'a Modbus", + "modbus-add-server-prompt":"Proszę dodać serwer/slave'a Modbus", + "modbus-transport":"Transport", + "modbus-tcp-reconnect":"Automatyczne ponowne połączenie", + "modbus-rtu-over-tcp":"RTU przez TCP", + "modbus-port-name":"Nazwa portu szeregowego", + "modbus-encoding":"Kodowanie", + "modbus-parity":"Parzystość", + "modbus-baudrate":"Prędkość transmisji", + "modbus-databits":"Bity danych", + "modbus-stopbits":"Bity stopu", + "modbus-databits-range":"Bity danych powinny mieć zakres od 7 do 8.", + "modbus-stopbits-range":"Bity stopu powinny mieć zakres od 1 do 2.", + "modbus-unit-id":"Identyfikator jednostki", + "modbus-unit-id-range":"Identyfikator jednostki powinien mieć zakres od 1 do 247.", + "modbus-device-name":"Nazwa urządzenia", + "modbus-poll-period":"Okres sondowania (ms)", + "modbus-attributes-poll-period":"Okres sondowania atrybutów (ms)", + "modbus-timeseries-poll-period":"Okres sondowania szeregów czasowych (ms)", + "modbus-poll-period-range":"Okres sondowania powinien być wartością dodatnią.", + "modbus-tag":"Tag", + "modbus-function":"Funkcja", + "modbus-register-address":"Adres rejestru", + "modbus-register-address-range":"Adres rejestru powinien mieć zakres od 0 do 65535.", + "modbus-register-bit-index":"Indeks bitu", + "modbus-register-bit-index-range":"Indeks bitu powinien mieć zakres od 0 do 15.", + "modbus-register-count":"Liczba rejestrów", + "modbus-register-count-range":"Liczba rejestrów powinna być wartością dodatnią.", + "modbus-byte-order":"Kolejność bajtów", + "sync":{ + "status":"Status", + "sync":"Synchronizowane", + "not-sync":"Niesynchronizowane", + "last-sync-time":"Ostatnia synchronizacja", + "not-available":"Niedostępne" + }, + "export-extensions-configuration":"Eksportuj konfigurację rozszerzeń", + "import-extensions-configuration":"Importuj konfigurację rozszerzeń", + "import-extensions":"Importuj rozszerzenia", + "import-extension":"Importuj rozszerzenie", + "export-extension":"Eksportuj rozszerzenie", + "file":"Plik rozszerzeń", + "invalid-file-error":"Błąd - nieprawidłowy plik rozszerzenia" + }, + "feature":{ + "advanced-features":"Zaawansowane funkcje" + }, + "filter":{ + "add":"Dodaj filtr", + "edit":"Edytuj filtr", + "name":"Nazwa filtra", + "name-required":"Wymagana nazwa filtra.", + "duplicate-filter":"Filtr o tej samej nazwie już istnieje.", + "filters":"Filtry", + "unable-delete-filter-title":"Nie można usunąć filtra", + "unable-delete-filter-text":"Nie można usunąć filtra '{{filter}}', ponieważ jest używany przez następujące widżety:
{{widgetsList}}", + "duplicate-filter-error":"Znaleziono zduplikowany filtr '{{filter}}'.
Filtry muszą być unikalne w obrębie pulpitu nawigacyjnego.", + "missing-key-filters-error":"Kluczowe filtry są brakujące w filtrze '{{filter}}'.", + "filter":"Filtr", + "editable":"Możliwość edycji", + "no-filters-found":"Nie znaleziono filtrów.", + "no-filter-text":"Brak określonego filtra", + "add-filter-prompt":"Proszę dodać filtr", + "no-filter-matching":"'{{filter}}' nie znaleziono.", + "create-new-filter":"Utwórz nowy!", + "create-new":"Utwórz nowy", + "filter-required":"Filtr jest wymagany.", + "operation":{ + "operation":"Operacja", + "equal":"równa się", + "not-equal":"nie równa się", + "starts-with":"zaczyna się od", + "ends-with":"kończy się na", + "contains":"zawiera", + "not-contains":"nie zawiera", + "greater":"większa niż", + "less":"mniejsza niż", + "greater-or-equal":"większa lub równa", + "less-or-equal":"mniejsza lub równa", + "and":"i", + "or":"lub", + "in":"w", + "not-in":"nie w" + }, + "ignore-case":"ignoruj wielkość liter", + "value":"Wartość", + "remove-filter":"Usuń filtr", + "duplicate-filter-action":"Zduplikuj filtr", + "preview":"Podgląd filtra", + "no-filters":"Brak skonfigurowanych filtrów", + "add-filter":"Dodaj filtr", + "add-complex-filter":"Dodaj filtr złożony", + "add-complex":"Dodaj złożony", + "complex-filter":"Filtr złożony", + "edit-complex-filter":"Edytuj filtr złożony", + "edit-filter-user-params":"Edytuj parametry użytkownika predykatu filtra", + "filter-user-params":"Parametry użytkownika predykatu filtra", + "user-parameters":"Parametry użytkownika", + "display-label":"Etykieta do wyświetlenia", + "autogenerated-label":"Automatycznie generowana etykieta", + "order-priority":"Priorytet porządkowania pola", + "key-filter":"Filtr klucza", + "key-filters":"Filtry klucza", + "key-name":"Nazwa klucza", + "key-name-required":"Wymagana jest nazwa klucza.", + "key-type":{ + "key-type":"Typ klucza", + "attribute":"Atrybut", + "timeseries":"Szereg czasowy", + "entity-field":"Pole encji", + "constant":"Stała", + "client-attribute":"Atrybut klienta", + "server-attribute":"Atrybut serwera", + "shared-attribute":"Atrybut wspólny" + }, + "value-type":{ + "value-type":"Value type", + "string":"String", + "numeric":"Numeric", + "boolean":"Boolean", + "date-time":"Datetime" + }, + "value-type-required":"Wymagany jest typ wartości klucza.", + "key-value-type-change-title":"Czy na pewno chcesz zmienić typ wartości klucza?", + "key-value-type-change-message":"Jeśli potwierdzisz nowy typ wartości, wszystkie wprowadzone filtry klucza zostaną usunięte.", + "no-key-filters":"Brak skonfigurowanych filtrów klucza", + "add-key-filter":"Dodaj filtr klucza", + "remove-key-filter":"Usuń filtr klucza", + "edit-key-filter":"Edytuj filtr klucza", + "date":"Data", + "time":"Czas", + "current-tenant":"Bieżący najemca", + "current-customer":"Bieżący klient", + "current-user":"Bieżący użytkownik", + "current-device":"Bieżące urządzenie", + "default-value":"Wartość domyślna", + "dynamic-source-type":"Typ źródła dynamicznego", + "dynamic-value":"Wartość dynamiczna", + "no-dynamic-value":"Brak wartości dynamicznej", + "source-attribute":"Atrybut źródła", + "switch-to-dynamic-value":"Przełącz na wartość dynamiczną", + "switch-to-default-value":"Przełącz na wartość domyślną", + "inherit-owner":"Dziedzicz od właściciela", + "source-attribute-not-set":"Jeśli atrybut źródła nie jest ustawiony" + }, + "fullscreen":{ + "expand":"Rozwiń na pełny ekran", + "exit":"Wyjdź z trybu pełnoekranowego", + "toggle":"Przełącz tryb pełnoekranowy", + "fullscreen":"Pełny ekran" + }, + "function":{ + "function":"Funkcja" + }, + "gateway":{ + "add-entry":"Dodaj konfigurację", + "advanced":"Zaawansowane", + "checking-device-activity":"Sprawdzanie aktywności urządzenia", + "command":"Komendy Docker", + "command-copied-message":"Komenda Docker została skopiowana do schowka", + "configuration":"Konfiguracja", + "connector-add":"Dodaj nowy konektor", + "connector-enabled":"Włącz konektor", + "connector-name":"Nazwa konektora", + "connector-name-required":"Nazwa konektora jest wymagana.", + "connector-type":"Typ konektora", + "connector-type-required":"Typ konektora jest wymagany.", + "connectors":"Konektory", + "connectors-config":"Konfiguracja konektorów", + "connectors-table-enabled":"Włączony", + "connectors-table-name":"Nazwa", + "connectors-table-type":"Typ", + "connectors-table-status":"Status", + "connectors-table-actions":"Akcje", + "connectors-table-key":"Klucz", + "connectors-table-class":"Klasa", + "rpc-command-send":"Wyślij", + "rpc-command-result":"Odpowiedź", + "rpc-command-edit-params":"Edytuj parametry", + "gateway-configuration":"Konfiguracja ogólna", + "docker-label":"Użyj poniższej instrukcji, aby uruchomić bramę IoT w Docker Compose z uwierzytelnieniem dla wybranego urządzenia", + "install-docker-compose":"Skorzystaj z instrukcji, aby pobrać, zainstalować i skonfigurować Docker Compose", + "download-configuration-file":"Pobierz plik konfiguracyjny", + "download-docker-compose":"Pobierz plik docker-compose.yml dla Twojej bramy", + "launch-gateway":"Uruchom bramę", + "launch-docker-compose":"Uruchom bramę, używając poniższej komendy w terminalu z folderu zawierającego plik docker-compose.yml", + "create-new-gateway":"Utwórz nową bramę", + "create-new-gateway-text":"Jesteś pewien, że chcesz utworzyć nową bramę o nazwie: '{{gatewayName}}'?", + "created-time":"Czas utworzenia", + "configuration-delete-dialog-header":"Konfiguracje zostaną usunięte", + "configuration-delete-dialog-body":"Wyłączenie Konfiguracji Zdalnej jest możliwe tylko przy fizycznym dostępie do Bramy. Wszystkie wcześniejsze konfiguracje zostaną usunięte.

\nAby wyłączyć konfigurację, wprowadź poniżej nazwę Bramy", + "configuration-delete-dialog-input":"Nazwa Bramy", + "configuration-delete-dialog-input-required":"Nazwa Bramy jest obowiązkowa", + "configuration-delete-dialog-confirm":"Wyłącz", + "delete":"Usuń konfigurację", + "download-tip":"Pobierz plik konfiguracyjny", + "drop-file":"Upuść plik tutaj lub", + "gateway":"Brama", + "gateway-exists":"Urządzenie o tej samej nazwie już istnieje.", + "gateway-name":"Nazwa Bramy", + "gateway-name-required":"Nazwa Bramy jest wymagana.", + "gateway-saved":"Konfiguracja Bramy została pomyślnie zapisana.", + "grpc":"GRPC", + "grpc-keep-alive-timeout":"Limit czasu Keep Alive (w ms)", + "grpc-keep-alive-timeout-required":"Limit czasu Keep Alive jest wymagany", + "grpc-keep-alive-timeout-min":"Limit czasu Keep Alive nie może być mniejszy niż 1", + "grpc-keep-alive-timeout-pattern":"Limit czasu Keep Alive jest nieprawidłowy", + "grpc-keep-alive":"Keep Alive (w ms)", + "grpc-keep-alive-required":"Keep Alive jest wymagany", + "grpc-keep-alive-min":"Keep Alive nie może być mniejszy niż 1", + "grpc-keep-alive-pattern":"Keep Alive jest nieprawidłowy", + "grpc-min-time-between-pings":"Minimalny czas między pingami (w ms)", + "grpc-min-time-between-pings-required":"Minimalny czas między pingami jest wymagany", + "grpc-min-time-between-pings-min":"Minimalny czas między pingami nie może być mniejszy niż 1", + "grpc-min-time-between-pings-pattern":"Minimalny czas między pingami jest nieprawidłowy", + "grpc-min-ping-interval-without-data":"Minimalny interwał pingowania bez danych (w ms)", + "grpc-min-ping-interval-without-data-required":"Minimalny interwał pingowania bez danych jest wymagany", + "grpc-min-ping-interval-without-data-min":"Minimalny interwał pingowania bez danych nie może być mniejszy niż 1", + "grpc-min-ping-interval-without-data-pattern":"Minimalny interwał pingowania bez danych jest nieprawidłowy", + "grpc-max-pings-without-data":"Maksymalna liczba pingów bez danych", + "grpc-max-pings-without-data-required":"Maksymalna liczba pingów bez danych jest wymagana", + "grpc-max-pings-without-data-min":"Maksymalna liczba pingów bez danych nie może być mniejsza niż 1", + "grpc-max-pings-without-data-pattern":"Maksymalna liczba pingów bez danych jest nieprawidłowa", + "inactivity-check-period-seconds":"Okres sprawdzania braku aktywności (w sekundach)", + "inactivity-check-period-seconds-required":"Okres sprawdzania braku aktywności jest wymagany", + "inactivity-check-period-seconds-min":"Okres sprawdzania braku aktywności nie może być mniejszy niż 1", + "inactivity-check-period-seconds-pattern":"Okres sprawdzania braku aktywności jest nieprawidłowy", + "inactivity-timeout-seconds":"Okres braku aktywności (w sekundach)", + "inactivity-timeout-seconds-required":"Okres braku aktywności jest wymagany", + "inactivity-timeout-seconds-min":"Okres braku aktywności nie może być mniejszy niż 1", + "inactivity-timeout-seconds-pattern":"Okres braku aktywności jest nieprawidłowy", + "json-parse":"Nieprawidłowy format JSON.", + "json-required":"To pole nie może być puste.", + "logs":{ + "logs":"Logi", + "days":"dni", + "hours":"godzin", + "minutes":"minut", + "seconds":"sekund", + "date-format":"Format daty", + "date-format-required":"Wymagany format daty", + "log-format":"Format logów", + "log-type":"Typ logu", + "log-format-required":"Wymagany format logów", + "remote":"Zdalne logowanie", + "remote-logs":"Zdalne logi", + "local":"Lokalne logowanie", + "level":"Poziom logowania", + "file-path":"Ścieżka pliku", + "file-path-required":"Wymagana ścieżka pliku", + "saving-period":"Okres zapisywania logów", + "saving-period-min":"Okres zapisywania logów nie może być krótszy niż 1", + "saving-period-required":"Wymagany okres zapisywania logów", + "backup-count":"Liczba kopii zapasowych", + "backup-count-min":"Liczba kopii zapasowych nie może być mniejsza niż 1", + "backup-count-required":"Wymagana liczba kopii zapasowych" + }, + "min-pack-send-delay":"Minimalny opóźnienie wysyłki paczki (w ms)", + "min-pack-send-delay-required":"Wymagane minimalne opóźnienie wysyłki paczki", + "min-pack-send-delay-min":"Minimalne opóźnienie wysyłki paczki nie może być mniejsze niż 0", + "no-connectors":"Brak konektorów", + "no-data":"Brak konfiguracji", + "no-gateway-found":"Nie znaleziono bramy.", + "no-gateway-matching":"Brak dopasowania do '{{item}}'", + "path-logs":"Ścieżka do plików dziennika", + "path-logs-required":"Wymagana ścieżka", + "permit-without-calls":"Zezwalaj na keep alive bez wywołań", + "remote":"Zdalna konfiguracja", + "remote-logging-level":"Poziom logowania", + "remove-entry":"Usuń konfigurację", + "remote-shell":"Zdalny shell", + "remote-configuration":"Zdalna konfiguracja", + "other":"Inne", + "save-tip":"Zapisz plik konfiguracyjny", + "security-type":"Typ zabezpieczeń", + "security-types":{ + "access-token":"Token dostępu", + "username-password":"Nazwa użytkownika i hasło", + "tls":"TLS", + "tls-access-token":"TLS + Token dostępu", + "tls-private-key":"TLS + Klucz prywatny" + }, + "server-port":"Port serwera", + "statistics":{ + "statistic":"Statystyka", + "statistics":"Statystyki", + "statistic-commands-empty":"Brak dostępnych statystyk", + "commands":"Polecenia", + "send-period":"Okres wysyłania statystyk (w sekundach)", + "send-period-required":"Okres wysyłania statystyk jest wymagany", + "send-period-min":"Okres wysyłania statystyk nie może być krótszy niż 60", + "send-period-pattern":"Okres wysyłania statystyk jest nieprawidłowy", + "check-connectors-configuration":"Sprawdź konfigurację łączników (w sekundach)", + "check-connectors-configuration-required":"Sprawdzenie konfiguracji łączników jest wymagane", + "check-connectors-configuration-min":"Sprawdzenie konfiguracji łączników nie może być krótsze niż 1", + "check-connectors-configuration-pattern":"Sprawdzenie konfiguracji łączników jest nieprawidłowe", + "add":"Dodaj polecenie", + "timeout":"Limit czasu", + "timeout-ms":"Limit czasu (w ms)", + "timeout-required":"Limit czasu jest wymagany", + "timeout-min":"Limit czasu nie może być krótszy niż 1", + "timeout-pattern":"Limit czasu jest nieprawidłowy", + "attribute-name":"Nazwa atrybutu", + "attribute-name-required":"Nazwa atrybutu jest wymagana", + "command":"Polecenie", + "command-required":"Polecenie jest wymagane", + "command-pattern":"Polecenie jest nieprawidłowe", + "remove":"Usuń polecenie" + }, + "storage":"Magazyn", + "storage-max-file-records":"Maksymalna liczba rekordów w pliku", + "storage-max-files":"Maksymalna liczba plików", + "storage-max-files-min":"Minimalna liczba wynosi 1.", + "storage-max-files-pattern":"Liczba jest nieprawidłowa.", + "storage-max-files-required":"Wymagana jest liczba.", + "storage-max-records":"Maksymalna liczba rekordów w magazynie", + "storage-max-records-min":"Minimalna liczba rekordów wynosi 1.", + "storage-max-records-pattern":"Liczba jest nieprawidłowa.", + "storage-max-records-required":"Wymagana jest maksymalna liczba rekordów.", + "storage-read-record-count":"Liczba odczytanych rekordów w magazynie", + "storage-read-record-count-min":"Minimalna liczba rekordów wynosi 1.", + "storage-read-record-count-pattern":"Liczba jest nieprawidłowa.", + "storage-read-record-count-required":"Wymagana jest liczba odczytanych rekordów.", + "storage-max-read-record-count":"Maksymalna liczba odczytanych rekordów w magazynie", + "storage-max-read-record-count-min":"Minimalna liczba rekordów wynosi 1.", + "storage-max-read-record-count-pattern":"Liczba jest nieprawidłowa.", + "storage-max-read-record-count-required":"Wymagana jest maksymalna liczba odczytanych rekordów.", + "storage-data-folder-path":"Ścieżka folderu danych", + "storage-data-folder-path-required":"Wymagana jest ścieżka folderu danych.", + "storage-pack-size":"Maksymalny rozmiar pakietu zdarzeń", + "storage-pack-size-min":"Minimalna liczba wynosi 1.", + "storage-pack-size-pattern":"Liczba jest nieprawidłowa.", + "storage-pack-size-required":"Wymagany jest maksymalny rozmiar pakietu zdarzeń.", + "storage-path":"Ścieżka magazynu", + "storage-path-required":"Wymagana jest ścieżka magazynu.", + "storage-type":"Typ magazynu", + "storage-types":{ + "file-storage":"Magazyn plików", + "memory-storage":"Magazyn pamięci", + "sqlite":"SQLite" + }, + "thingsboard":"ThingsBoard", + "general":"Ogólne", + "thingsboard-host":"Host ThingsBoard", + "thingsboard-host-required":"Host jest wymagany.", + "thingsboard-port":"Port ThingsBoard", + "thingsboard-port-max":"Maksymalny numer portu to 65535.", + "thingsboard-port-min":"Minimalny numer portu to 1.", + "thingsboard-port-pattern":"Port jest niepoprawny.", + "thingsboard-port-required":"Port jest wymagany.", + "tidy":"Porządkuj", + "tidy-tip":"Porządkuj konfigurację JSON", + "title-connectors-json":"Konfiguracja konektora {{typeName}}", + "tls-path-ca-certificate":"Ścieżka do certyfikatu CA na gatewayu", + "tls-path-client-certificate":"Ścieżka do certyfikatu klienta na gatewayu", + "messages-ttl-check-in-hours":"Sprawdź TTL wiadomości w godzinach", + "messages-ttl-check-in-hours-required":"Sprawdzenie TTL wiadomości w godzinach jest wymagane.", + "messages-ttl-check-in-hours-min":"Minimalna liczba to 1.", + "messages-ttl-check-in-hours-pattern":"Liczba jest niepoprawna.", + "messages-ttl-in-days":"TTL wiadomości w dniach", + "messages-ttl-in-days-required":"TTL wiadomości w dniach jest wymagane.", + "messages-ttl-in-days-min":"Minimalna liczba to 1.", + "messages-ttl-in-days-pattern":"Liczba jest niepoprawna.", + "mqtt-qos":"QoS", + "mqtt-qos-required":"QoS jest wymagane", + "mqtt-qos-range":"Wartości QoS są w zakresie od 0 do 1", + "tls-path-private-key":"Ścieżka do prywatnego klucza na gatewayu", + "toggle-fullscreen":"Przełącz pełny ekran", + "transformer-json-config":"Konfiguracja JSON*", + "update-config":"Dodaj/aktualizuj konfigurację JSON", + "hints":{ + "remote-configuration":"Umożliwia zdalną konfigurację i zarządzanie bramką", + "remote-shell":"Umożliwia zdalne sterowanie systemem operacyjnym bramki z widżetu Zdalnej Powłoki", + "host":"Nazwa hosta lub adres IP serwera ThingsBoard", + "port":"Port usługi MQTT na serwerze ThingsBoard", + "token":"Token dostępowy dla bramki na serwerze ThingsBoard", + "client-id":"Identyfikator klienta MQTT dla bramki na serwerze ThingsBoard", + "username":"Nazwa użytkownika MQTT dla bramki na serwerze ThingsBoard", + "password":"Hasło MQTT dla bramki na serwerze ThingsBoard", + "ca-cert":"Ścieżka do pliku certyfikatu CA", + "date-form":"Format daty w komunikatach dziennika", + "data-folder":"Ścieżka do folderu, który będzie zawierał dane (względna lub bezwzględna)", + "log-format":"Format komunikatu dziennika", + "remote-log":"Umożliwia zdalne logowanie i odczyt dziennika z bramki", + "backup-count":"Jeśli liczba kopii zapasowych > 0, po dokonaniu zmiany rolki, nie więcej niż określona liczba plików kopii zapasowych jest przechowywana - najstarsze zostaną usunięte", + "storage":"Zapewnia konfigurację zapisu przychodzących danych przed ich wysłaniem na platformę", + "max-file-count":"Maksymalna liczba plików, które zostaną utworzone", + "max-read-count":"Liczba wiadomości do pobrania z magazynu i wysłania do ThingsBoard", + "max-records":"Maksymalna liczba rekordów przechowywanych w jednym pliku", + "read-record-count":"Liczba wiadomości do pobrania z magazynu i wysłania do ThingsBoard", + "max-records-count":"Maksymalna liczba danych w magazynie przed wysłaniem do ThingsBoard", + "ttl-check-hour":"Jak często brama sprawdza dane pod kątem przestarzałości", + "ttl-messages-day":"Maksymalna liczba dni, przez które dane będą przechowywane w magazynie", + "commands":"Polecenia do zbierania dodatkowych statystyk", + "attribute":"Klucz telemetrii statystyk", + "timeout":"Limit czasu na wykonanie polecenia", + "command":"Wynik wykonania polecenia, będzie używany jako wartość telemetrii", + "check-device-activity":"Umożliwia monitorowanie aktywności każdego podłączonego urządzenia", + "inactivity-timeout":"Czas po którym brama rozłączy urządzenie", + "inactivity-period":"Okresowość sprawdzania aktywności urządzenia", + "minimal-pack-delay":"Opóźnienie między wysyłką paczek wiadomości (zmniejszenie tego ustawienia zwiększa zużycie procesora)", + "qos":"Jakość usługi w komunikacji MQTT (0 - przynajmniej raz, 1 - co najmniej raz)", + "server-port":"Port sieciowy, na którym serwer GRPC nasłuchuje nadchodzących połączeń.", + "grpc-keep-alive-timeout":"Maksymalny czas oczekiwania serwera na odpowiedź keepalive ping przed uznaniem połączenia za martwe.", + "grpc-keep-alive":"Czas między dwoma kolejnymi wiadomościami keepalive ping, gdy nie ma aktywnego wywołania RPC.", + "grpc-min-time-between-pings":"Minimalny czas oczekiwania serwera między wysłaniem wiadomości keepalive ping.", + "grpc-max-pings-without-data":"Maksymalna liczba wiadomości keepalive ping, które serwer może wysłać, nie odbierając żadnych danych, zanim uzna połączenie za martwe.", + "grpc-min-ping-interval-without-data":"Minimalny czas oczekiwania serwera między wysłaniem wiadomości keepalive ping, gdy nie są przesyłane ani odbierane żadne dane.", + "permit-without-calls":"Zezwalaj serwerowi na utrzymanie połączenia GRPC aktywnego, nawet gdy nie ma aktywnych wywołań RPC." + } + }, + "grid":{ + "delete-item-title":"Czy na pewno chcesz usunąć ten element?", + "delete-item-text":"Bądź ostrożny, po potwierdzeniu ten element i wszystkie powiązane dane staną się nieodwracalne.", + "delete-items-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 element} other {# elementów} }?", + "delete-items-action-title":"Usuń { count, plural, =1 {1 element} other {# elementów} }", + "delete-items-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane elementy zostaną usunięte, a wszystkie powiązane dane staną się nieodwracalne.", + "add-item-text":"Dodaj nowy element", + "no-items-text":"Nie znaleziono elementów", + "item-details":"Szczegóły elementu", + "delete-item":"Usuń element", + "delete-items":"Usuń elementy", + "scroll-to-top":"Przewiń do góry" + }, + "help":{ + "goto-help-page":"Przejdź do strony pomocy", + "show-help":"Pokaż pomoc" + }, + "home":{ + "home":"Strona główna", + "profile":"Profil", + "logout":"Wyloguj się", + "menu":"Menu", + "avatar":"Awatar", + "open-user-menu":"Otwórz menu użytkownika" + }, + "file-input":{ + "browse-file":"Przeglądaj plik", + "browse-files":"Przeglądaj pliki" + }, + "image":{ + "gallery":"Galeria obrazów", + "search":"Wyszukaj obraz", + "selected-images":"{ count, plural, =1 {1 obraz} other {# obrazy} } wybranych", + "created-time":"Czas utworzenia", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "resolution":"Rozdzielczość", + "size":"Rozmiar", + "system":"System", + "download-image":"Pobierz obraz", + "export-image":"Eksportuj obraz do JSON", + "import-image":"Importuj obraz z JSON", + "upload-image":"Prześlij obraz", + "edit-image":"Edytuj obraz", + "image-details":"Szczegóły obrazu", + "no-images":"Nie znaleziono obrazów", + "delete-image":"Usuń obraz", + "delete-image-title":"Czy na pewno chcesz usunąć obraz '{{imageTitle}}'?", + "delete-image-text":"Bądź ostrożny, po potwierdzeniu obraz stanie się nieodwracalnie utracony.", + "delete-images-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 obraz} other {# obrazy} }?", + "delete-images-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane obrazy zostaną usunięte, a wszelkie powiązane dane staną się nieodwracalnie utracone.", + "list-mode":"Widok listy", + "grid-mode":"Widok siatki", + "image-preview":"Podgląd obrazu", + "update-image":"Aktualizuj obraz", + "export-failed-error":"Nie można wyeksportować obrazu: {{error}}", + "image-json-file":"Plik JSON obrazu", + "invalid-image-json-file-error":"Nie można zaimportować obrazu z JSON: Nieprawidłowa struktura danych JSON obrazu.", + "image-is-in-use":"Obraz jest używany przez inne jednostki", + "images-are-in-use":"Obrazy są używane przez inne jednostki", + "image-is-in-use-text":"Obraz '{{title}}' nie został usunięty, ponieważ jest używany przez następujące jednostki:", + "images-are-in-use-text":"Nie wszystkie obrazy zostały usunięte, ponieważ są używane przez inne jednostki.
Możesz zobaczyć odwołane jednostki, klikając przycisk Odwołania w odpowiednim wierszu obrazu.
Jeśli nadal chcesz usunąć te obrazy, zaznacz je w tabeli poniżej i kliknij przycisk Usuń zaznaczone.", + "delete-image-in-use-text":"Jeśli nadal chcesz usunąć obraz, kliknij przycisk Usuń tak czy inaczej.", + "system-entities":"Jednostki systemowe:", + "entities":"jednostki:", + "references":"Odwołania", + "include-system-images":"Dołącz obrazy systemowe", + "clear-image":"Wyczyść obraz", + "no-image":"Brak obrazu", + "no-image-selected":"Nie wybrano obrazu", + "browse-from-gallery":"Przeglądaj z galerii", + "set-link":"Ustaw link", + "image-link":"Link obrazu", + "link":"Link", + "copy-image-link":"Kopiuj link obrazu", + "embed-image":"Osadź obraz", + "embed-to-html":"Osadź w HTML", + "embed-to-html-hint":"Ta funkcja umożliwia udostępnianie linku każdemu nieautoryzowanemu użytkownikowi.", + "embed-to-html-text":"Za pomocą poniższego fragmentu kodu możesz osadzić obraz w komponentach opartych na prostym HTML.
Takie komponenty obejmują widżety karty HTML, funkcje zawartości komórek itp.", + "embed-to-angular-template":"Osadź w szablonie Angular HTML", + "embed-to-angular-template-text":"Za pomocą poniższego fragmentu kodu możesz osadzić obraz w szablonie Angular HTML.
Takie komponenty obejmują widżet Markdown, sekcję HTML w edytorze widżetów, niestandardowe akcje itp." + }, + "image-input":{ + "drop-images-or":"Przeciągnij i upuść obrazy lub", + "drag-and-drop":"Przeciągnij i Upuść", + "or":"lub", + "browse":"Przeglądaj", + "no-images":"Brak wybranych obrazów", + "images":"obrazy" + }, + "import":{ + "no-file":"Nie wybrano pliku", + "drop-file":"Przeciągnij plik JSON lub kliknij, aby wybrać plik do przesłania.", + "drop-json-file-or":"Przeciągnij plik JSON lub", + "drop-file-csv":"Przeciągnij plik CSV lub kliknij, aby wybrać plik do przesłania.", + "drop-file-csv-or":"Przeciągnij plik CSV lub", + "column-value":"Wartość", + "column-title":"Tytuł", + "column-example":"Przykładowa wartość danych", + "column-key":"Atrybut/klucz telemetryczny", + "credentials":"Dane uwierzytelniające", + "csv-delimiter":"Separator CSV", + "csv-first-line-header":"Pierwsza linia zawiera nazwy kolumn", + "csv-update-data":"Aktualizuj atrybuty/telemetrię", + "details":"Szczegóły", + "import-csv-number-columns-error":"Plik powinien zawierać co najmniej dwie kolumny", + "import-csv-invalid-format-error":"Nieprawidłowy format pliku. Linia: '{{line}}'", + "column-type":{ + "name":"Nazwa", + "type":"Typ", + "label":"Etykieta", + "column-type":"Typ kolumny", + "client-attribute":"Atrybut klienta", + "shared-attribute":"Wspólny atrybut", + "server-attribute":"Atrybut serwera", + "timeseries":"Szereg czasowy", + "entity-field":"Pole encji", + "access-token":"Token dostępu", + "x509":"X.509", + "mqtt":{ + "client-id":"ID klienta MQTT", + "user-name":"Nazwa użytkownika MQTT", + "password":"Hasło MQTT" + }, + "lwm2m":{ + "client-endpoint":"Nazwa klienta LwM2M endpoint", + "security-config-mode":"Tryb konfiguracji bezpieczeństwa LwM2M", + "client-identity":"Tożsamość klienta LwM2M", + "client-key":"Klucz klienta LwM2M", + "client-cert":"Certyfikat publiczny klienta LwM2M", + "bootstrap-server-security-mode":"Tryb bezpieczeństwa serwera rozruchowego LwM2M", + "bootstrap-server-secret-key":"Tajny klucz serwera rozruchowego LwM2M", + "bootstrap-server-public-key-id":"Klucz publiczny lub identyfikator serwera rozruchowego LwM2M", + "lwm2m-server-security-mode":"Tryb bezpieczeństwa serwera LwM2M", + "lwm2m-server-secret-key":"Tajny klucz serwera LwM2M", + "lwm2m-server-public-key-id":"Klucz publiczny lub identyfikator serwera LwM2M" + }, + "snmp":{ + "host":"Host SNMP", + "port":"Port SNMP", + "version":"Wersja SNMP (v1, v2c lub v3)", + "community-string":"Ciąg społeczności SNMP" + }, + "isgateway":"Czy to jest Bramą", + "activity-time-from-gateway-device":"Czas aktywności urządzenia bramowego", + "description":"Opis", + "routing-key":"Klucz brzegowy", + "secret":"Sekret brzegowy" + }, + "stepper-text":{ + "select-file":"Wybierz plik", + "configuration":"Importuj konfigurację", + "column-type":"Wybierz typy kolumn", + "creat-entities":"Tworzenie nowych jednostek" + }, + "message":{ + "create-entities":"{{count}} nowych jednostek zostało pomyślnie utworzonych.", + "update-entities":"{{count}} jednostek zostało pomyślnie zaktualizowanych.", + "error-entities":"Wystąpił błąd podczas tworzenia {{count}} jednostek." + } + }, + "item":{ + "selected":"Wybrane" + }, + "js-func":{ + "no-return-error":"Funkcja musi zwrócić wartość!", + "return-type-mismatch":"Funkcja musi zwrócić wartość typu '{{type}}'!", + "tidy":"Porządek", + "mini":"Mini" + }, + "key-val":{ + "key":"Klucz", + "value":"Wartość", + "remove-entry":"Usuń wpis", + "add-entry":"Dodaj wpis", + "no-data":"Brak danych" + }, + "layout":{ + "layout":"Układ", + "layouts":"Układy", + "manage":"Zarządzaj układami", + "settings":"Ustawienia układu", + "color":"Kolor", + "main":"Główny", + "right":"Prawo", + "left":"Lewo", + "select":"Wybierz docelowy układ", + "percentage-width":"Szerokość procentowa (%)", + "fixed-width":"Szerokość stała (px)", + "left-width":"Szerokość lewej kolumny (%)", + "right-width":"Szerokość prawej kolumny (%)", + "pick-fixed-side":"Stała strona: ", + "layout-fixed-width":"Szerokość stała (px)", + "value-min-error":"Wartość musi być większa niż {{min}}{{unit}}", + "value-max-error":"Wartość musi być mniejsza niż {{max}}{{unit}}", + "layout-fixed-width-required":"Szerokość stała jest wymagana", + "right-width-percentage-required":"Wymagany jest procent szerokości prawej strony", + "left-width-percentage-required":"Wymagany jest procent szerokości lewej strony", + "divider":"Podziałka", + "right-side":"Układ prawej strony", + "left-side":"Układ lewej strony" + }, + "legend":{ + "direction":"Kierunek", + "position":"Pozycja", + "show-values":"Pokaż wartości", + "min-option":"Min", + "max-option":"Maks.", + "average-option":"Średnia", + "total-option":"Suma", + "latest-option":"Najnowsza", + "sort-legend":"Sortuj klucze danych w legendzie", + "show-max":"Pokaż maksymalną wartość", + "show-min":"Pokaż minimalną wartość", + "show-avg":"Pokaż średnią wartość", + "show-total":"Pokaż sumę wartości", + "show-latest":"Pokaż najnowszą wartość", + "settings":"Ustawienia legendy", + "min":"min", + "max":"maks.", + "avg":"śr.", + "total":"suma", + "latest":"najnowsza", + "comparison-time-ago":{ + "previousInterval":"(poprzedni przedział)", + "customInterval":"(niestandardowy przedział)", + "days":"(dzień temu)", + "weeks":"(tydzień temu)", + "months":"(miesiąc temu)", + "years":"(rok temu)" + }, + "label":"Etykieta", + "value":"Wartość" + }, + "login":{ + "login":"Zaloguj się", + "request-password-reset":"Zażądaj resetu hasła", + "reset-password":"Resetuj hasło", + "create-password":"Utwórz hasło", + "two-factor-authentication":"Autentykacja dwuskładnikowa", + "passwords-mismatch-error":"Wprowadzone hasła muszą być takie same!", + "password-again":"Powtórz hasło", + "sign-in":"Proszę się zalogować", + "username":"Nazwa użytkownika (e-mail)", + "remember-me":"Zapamiętaj mnie", + "forgot-password":"Zapomniałeś hasła?", + "password-reset":"Reset hasła", + "expired-password-reset-message":"Twoje dane uwierzytelniające wygasły! Proszę utworzyć nowe hasło.", + "new-password":"Nowe hasło", + "new-password-again":"Potwierdź nowe hasło", + "password-link-sent-message":"Link do resetowania hasła został wysłany", + "email":"E-mail", + "login-with":"Zaloguj się za pomocą {{name}}", + "or":"lub", + "error":"Błąd logowania", + "verify-your-identity":"Zweryfikuj swoją tożsamość", + "select-way-to-verify":"Wybierz sposób weryfikacji", + "resend-code":"Wyślij kod ponownie", + "resend-code-wait":"Ponownie wyślij kod za { time, plural, =1 {1 sekundę} other {# sekundy} }", + "try-another-way":"Spróbuj inną metodę", + "totp-auth-description":"Proszę podać kod z aplikacji autoryzacyjnej.", + "totp-auth-placeholder":"Kod", + "sms-auth-description":"Kod zabezpieczający został wysłany na Twój telefon pod numerem {{contact}}.", + "sms-auth-placeholder":"Kod SMS", + "email-auth-description":"Kod zabezpieczający został wysłany na Twój adres e-mailowy {{contact}}.", + "email-auth-placeholder":"Kod e-mailowy", + "backup-code-auth-description":"Proszę podać jeden z kodów zapasowych.", + "backup-code-auth-placeholder":"Kod zapasowy" + }, + "markdown":{ + "edit":"Edytuj", + "preview":"Podgląd", + "copy-code":"Kliknij, aby skopiować", + "copied":"Skopiowano!" + }, + "notification":{ + "action-button":"Przycisk akcji", + "action-type":"Typ akcji", + "active":"Aktywny", + "add-notification-recipients-group":"Dodaj grupę odbiorców powiadomień", + "add-notification-template":"Dodaj szablon powiadomienia", + "add-recipient":"Dodaj odbiorcę", + "add-recipients":"Dodaj odbiorców", + "add-rule":"Dodaj regułę", + "add-stage":"Dodaj etap", + "add-template":"Dodaj szablon", + "after":"Po", + "alarm-assignment-trigger-settings":"Ustawienia wyzwalacza przypisania alarmu", + "alarm-comment-trigger-settings":"Ustawienia wyzwalacza komentarza alarmu", + "alarm-trigger-settings":"Ustawienia wyzwalacza alarmu", + "all":"Wszystkie", + "api-feature-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich funkcji API", + "api-usage-trigger-settings":"Ustawienia wyzwalacza korzystania z API", + "new-platform-version-trigger-settings":"Ustawienia wyzwalacza nowej wersji platformy", + "rate-limits-trigger-settings":"Ustawienia wyzwalacza przekroczenia limitów szybkości", + "at-least-one-should-be-selected":"Przynajmniej jedna pozycja musi być zaznaczona", + "basic-settings":"Podstawowe ustawienia", + "button-text":"Tekst przycisku", + "button-text-required":"Tekst przycisku jest wymagany", + "button-text-max-length":"Tekst przycisku powinien być krótszy lub równy {{ length }} znaków", + "compose":"Komponuj", + "conversation":"Rozmowa", + "conversation-required":"Rozmowa jest wymagana", + "copy-notification-template":"Skopiuj szablon powiadomienia", + "copy-rule":"Skopiuj regułę", + "copy-template":"Skopiuj szablon", + "create-new":"Utwórz nowe", + "created":"Utworzono", + "delete-notification-text":"Uważaj, po potwierdzeniu powiadomienie stanie się nieodwracalne.", + "delete-notification-title":"Czy na pewno chcesz usunąć powiadomienie?", + "delete-notifications-text":"Uważaj, po potwierdzeniu powiadomienia staną się nieodwracalne.", + "delete-notifications-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 powiadomienie} other {# powiadomienia} }?", + "delete-recipient-text":"Uważaj, po potwierdzeniu odbiorca stanie się nieodwracalny.", + "delete-recipient-title":"Czy na pewno chcesz usunąć odbiorcę '{{recipientName}}'?", + "delete-recipients-text":"Uważaj, po potwierdzeniu odbiorcy staną się nieodwracalni.", + "delete-recipients-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 odbiorcę} other {# odbiorców} }?", + "delete-request-text":"Uważaj, po potwierdzeniu żądanie stanie się nieodwracalne.", + "delete-request-title":"Czy na pewno chcesz usunąć żądanie?", + "delete-requests-text":"Uważaj, po potwierdzeniu żądania staną się nieodwracalne.", + "delete-requests-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 żądanie} other {# żądań} }?", + "delete-rule-text":"Uważaj, po potwierdzeniu reguła stanie się nieodwracalna.", + "delete-rule-title":"Czy na pewno chcesz usunąć regułę '{{ruleName}}'?", + "delete-rules-text":"Uważaj, po potwierdzeniu reguły staną się nieodwracalne.", + "delete-rules-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 regułę} other {# reguł} }?", + "delete-template-text":"Uważaj, po potwierdzeniu szablon stanie się nieodwracalny.", + "delete-template-title":"Czy na pewno chcesz usunąć szablon '{{templateName}}'?", + "delete-templates-text":"Uważaj, po potwierdzeniu szablony staną się nieodwracalne.", + "delete-templates-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 szablon} other {# szablonów} }?", + "deleted":"Usunięto", + "delivery-method":{ + "delivery-method":"Metoda dostarczania", + "email":"Email", + "email-preview":"Podgląd powiadomienia email", + "slack":"Slack", + "slack-preview":"Podgląd powiadomienia Slack", + "microsoft-teams":"Microsoft Teams", + "microsoft-teams-preview":"Podgląd powiadomienia Microsoft Teams", + "sms":"SMS", + "sms-preview":"Podgląd powiadomienia SMS", + "web":"Web", + "web-preview":"Podgląd powiadomienia web" + }, + "delivery-method-not-configure-click":"Metoda dostarczania nie jest skonfigurowana. Kliknij, aby skonfigurować.", + "delivery-method-not-configure-contact":"Metoda dostarczania nie jest skonfigurowana. Skontaktuj się z administratorem systemu.", + "delivery-methods":"Metody dostarczania", + "description":"Opis", + "device-activity-trigger-settings":"Ustawienia wyzwalacza aktywności urządzenia", + "device-list-rule-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich urządzeń", + "device-profiles-list-rule-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich profili urządzeń", + "disabled":"Wyłączone", + "edit-notification-recipients-group":"Edytuj grupę odbiorców powiadomień", + "edit-notification-template":"Edytuj szablon powiadomień", + "edit-rule":"Edytuj regułę", + "edit-template":"Edytuj szablon", + "enabled":"Włączone", + "entities-limit-trigger-settings":"Ustawienia wyzwalacza limitu encji", + "entity-action-trigger-settings":"Ustawienia wyzwalacza akcji encji", + "entity-type":"Typ encji", + "escalation-chain":"Łańcuch eskalacji", + "failed-send":"Błędy wysyłki", + "fails":"{ count, plural, =1 {1 błąd} other {# błędów} }", + "filter":"Filtr", + "first-recipient":"Pierwszy odbiorca", + "inactive":"Nieaktywne", + "inbox":"Skrzynka odbiorcza", + "notification-inbox":"Powiadomienia / Skrzynka odbiorcza", + "input-field-support-templatization":"Pole wejściowe obsługuje szablonowanie.", + "input-fields-support-templatization":"Pola wejściowe obsługują szablonowanie.", + "link":"Odnośnik", + "link-required":"Odnośnik jest wymagany", + "link-type":{ + "dashboard":"Otwórz pulpit nawigacyjny", + "link":"Otwórz link URL" + }, + "loading-notifications":"Ładowanie powiadomień...", + "management":"Zarządzanie powiadomieniami", + "mark-all-as-read":"Oznacz wszystkie jako przeczytane", + "mark-as-read":"Oznacz jako przeczytane", + "message":"Wiadomość", + "message-required":"Wiadomość jest wymagana", + "message-max-length":"Wiadomość powinna mieć mniej niż {{ length }} znaków", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana", + "new-notification":"Nowe powiadomienie", + "no-inbox-notification":"Nie znaleziono powiadomień", + "no-notification-request":"Brak zapytania o powiadomienie", + "no-notification-templates":"Nie znaleziono szablonów powiadomień", + "no-notifications-yet":"Brak powiadomień", + "no-recipients-notification":"Powiadomienie bez odbiorców", + "no-rule":"Nie skonfigurowano reguły", + "no-rules-notification":"Brak powiadomienia o regułach", + "no-severity-found":"Nie znaleziono powagi", + "no-severity-matching":"Nie znaleziono '{{severity}}'.", + "no-template-matching":"Nie znaleziono zasobów pasujących do '{{template}}'.", + "not-found-slack-recipient":"Nie znaleziono odbiorcy Slack", + "notification":"Powiadomienie", + "notification-center":"Centrum powiadomień", + "notify":"powiadomić", + "notify-again":"Powiadom ponownie", + "notify-alarm-action":{ + "acknowledged":"Alarm potwierdzony", + "assigned":"Alarm przypisany", + "cleared":"Alarm wyczyszczony", + "created":"Alarm utworzony", + "severity-changed":"Zmieniono powagę alarmu", + "unassigned":"Alarm nieprzypisany" + }, + "notify-on":"Powiadamiaj o", + "notify-on-comment-update":"Powiadamiaj o aktualizacji komentarza", + "notify-on-required":"Wymagane jest powiadomienie o", + "notify-on-unassign":"Powiadamiaj o cofnięciu przypisania", + "notify-only-user-comments":"Powiadamiaj tylko o komentarzach użytkownika", + "only-rule-chain-lifecycle-failures":"Tylko niepowodzenia cyklu życia łańcucha reguł", + "only-rule-node-lifecycle-failures":"Tylko niepowodzenia cyklu życia węzła reguły", + "platform-users":"Użytkownicy platformy", + "rate-limits":"Limity częstotliwości", + "rate-limits-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich limitów częstotliwości", + "recipient":"Odbiorca", + "recipient-group":"Grupa odbiorców", + "recipient-type":{ + "affected-tenant-administrators":"Dotknięci administratorzy najemcy", + "affected-user":"Dotknięty użytkownik", + "all-users":"Wszyscy użytkownicy", + "customer-users":"Użytkownicy klientów", + "system-administrators":"Administratorzy systemu", + "tenant-administrators":"Administratorzy najemcy", + "user-filters":"Filtr użytkowników", + "user-list":"Lista użytkowników", + "users-entity-owner":"Użytkownicy właściciela jednostki" + }, + "recipients":"Odbiorcy", + "notification-recipients":"Powiadomienia / Odbiorcy", + "recipients-count":"{ count, plural, =1 {1 odbiorca} other {# odbiorców} }", + "recipients-required":"Odbiorcy są wymagani", + "refresh-allow-delivery-method":"Odśwież metodę dostarczania", + "request-search":"Wyszukaj żądanie", + "request-status":{ + "processing":"Przetwarzanie", + "scheduled":"Zaplanowane", + "sent":"Wysłane" + }, + "review":"Recenzja", + "rule":"Reguła", + "rule-chain-list-rule-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich łańcuchów reguł", + "rule-engine-events-trigger-settings":"Ustawienia wyzwalaczy zdarzeń silnika reguł", + "rule-engine-filter":"Filtr silnika reguł", + "rule-name":"Nazwa reguły", + "rule-name-required":"Nazwa jest wymagana", + "rule-disable":"Wyłącz regułę powiadomienia", + "rule-enable":"Włącz regułę powiadomienia", + "rule-node-filter":"Filtr węzła reguły", + "rules":"Reguły", + "notification-rules":"Powiadomienia / Reguły", + "scheduler-later":"Zaplanuj na później", + "search-notification":"Wyszukaj powiadomienia", + "search-recipients":"Wyszukaj odbiorców", + "search-rules":"Wyszukaj reguły", + "search-templates":"Wyszukaj szablony", + "see-documentation":"Zobacz dokumentację", + "selected-notifications":"{ count, plural, =1 {1 powiadomienie} other {# powiadomień} } wybrano", + "selected-recipients":"{ count, plural, =1 {1 odbiorca} other {# odbiorców} } wybrano", + "selected-requests":"{ count, plural, =1 {1 żądanie} other {# żądań} } wybrano", + "selected-rules":"{ count, plural, =1 {1 reguła} other {# reguł} } wybrano", + "selected-template":"{ count, plural, =1 {1 szablon} other {# szablonów} } wybrano", + "send-notification":"Wyślij powiadomienie", + "sent":"Wysłane", + "notification-sent":"Powiadomienia / Wysłane", + "set-entity-from-notification":"Ustaw jednostkę z powiadomienia na stan pulpitu nawigacyjnego", + "slack-chanel-type":"Typ kanału Slack", + "slack-chanel-types":{ + "direct":"Wiadomość bezpośrednia", + "private-channel":"Prywatny kanał", + "public-channel":"Publiczny kanał" + }, + "start-from-scratch":"Rozpocznij od początku", + "status":"Status", + "stop-escalation-alarm-status-become":"Zatrzymaj eskalację, gdy status alarmu stanie się:", + "subject":"Temat", + "subject-required":"Temat jest wymagany", + "template":"Szablon", + "template-name":"Nazwa szablonu", + "template-required":"Szablon jest wymagany", + "template-type":{ + "alarm":"Alarm", + "alarm-assignment":"Przypisanie alarmu", + "alarm-comment":"Komentarz do alarmu", + "api-usage-limit":"Limit użycia API", + "device-activity":"Aktywność urządzenia", + "entities-limit":"Limit jednostek", + "entity-action":"Akcja jednostki", + "general":"Ogólny", + "rule-engine-lifecycle-event":"Zdarzenie cyklu życia silnika reguł", + "rule-node":"Węzeł reguły", + "new-platform-version":"Nowa wersja platformy", + "rate-limits":"Przekroczono limity częstotliwości" + }, + "templates":"Szablony", + "notification-templates":"Powiadomienia / Szablony", + "tenant-profiles-list-rule-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich profili najemców", + "tenants-list-rule-hint":"Jeśli pole jest puste, wyzwalacz zostanie zastosowany do wszystkich najemców", + "threshold":"Próg", + "theme-color":"Kolor motywu", + "time":"Czas", + "track-rule-node-events":"Śledź zdarzenia węzła reguły", + "trigger":{ + "alarm":"Alarm", + "alarm-assignment":"Przypisanie alarmu", + "alarm-comment":"Komentarz do alarmu", + "api-usage-limit":"Limit użycia API", + "device-activity":"Aktywność urządzenia", + "entities-limit":"Limit jednostek", + "entity-action":"Akcja jednostki", + "rule-engine-lifecycle-event":"Zdarzenie cyklu życia silnika reguł", + "new-platform-version":"Nowa wersja platformy", + "rate-limits":"Przekroczono limity częstotliwości", + "trigger":"Wyzwalacz", + "trigger-required":"Wymagany jest wyzwalacz" + }, + "type":"Typ", + "unread":"Nieprzeczytane", + "updated":"Zaktualizowane", + "use-template":"Użyj szablonu", + "view-all":"Pokaż wszystko", + "warning":"Ostrzeżenie", + "webhook-url":"URL Webhooka", + "webhook-url-required":"URL Webhooka jest wymagany", + "channel-name":"Nazwa kanału", + "channel-name-required":"Nazwa kanału jest wymagana", + "settings":{ + "notification-settings":"Ustawienia powiadomień", + "reset-all":"Zresetuj wszystkie ustawienia", + "reset-all-title":"Czy na pewno chcesz zresetować formularz?", + "reset-all-text":"Po potwierdzeniu, formularz ustawień zostanie zresetowany do domyślnej wartości i zapisany.", + "type":"Typ", + "enable-all":"Włącz wszystkie", + "disable-all":"Wyłącz wszystkie", + "delivery-not-configured":"Metoda dostarczania nie jest skonfigurowana" + } + }, + "ota-update":{ + "add":"Dodaj pakiet", + "assign-firmware":"Przypisane oprogramowanie", + "assign-firmware-required":"Przypisane oprogramowanie jest wymagane", + "assign-software":"Przypisane oprogramowanie", + "assign-software-required":"Przypisane oprogramowanie jest wymagane", + "auto-generate-checksum":"Automatycznie generuj sumę kontrolną", + "checksum":"Suma kontrolna", + "checksum-hint":"Jeśli suma kontrolna jest pusta, zostanie wygenerowana automatycznie", + "checksum-algorithm":"Algorytm sumy kontrolnej", + "checksum-copied-message":"Suma kontrolna pakietu została skopiowana do schowka", + "change-firmware":"Zmiana oprogramowania może spowodować aktualizację { count, plural, =1 {1 urządzenia} other {# urządzeń} }.", + "change-software":"Zmiana oprogramowania może spowodować aktualizację { count, plural, =1 {1 urządzenia} other {# urządzeń} }.", + "chose-compatible-device-profile":"Wgrany pakiet będzie dostępny tylko dla urządzeń z wybranym profilem.", + "chose-firmware-distributed-device":"Wybierz oprogramowanie, które zostanie rozprowadzone na urządzenia", + "chose-software-distributed-device":"Wybierz oprogramowanie, które zostanie rozprowadzone na urządzenia", + "content-type":"Typ zawartości", + "copy-checksum":"Kopiuj sumę kontrolną", + "copy-direct-url":"Kopiuj bezpośredni adres URL", + "copyId":"Kopiuj identyfikator pakietu", + "copied":"Skopiowane!", + "delete":"Usuń pakiet", + "delete-ota-update-text":"Bądź ostrożny, po potwierdzeniu aktualizacja OTA stanie się nieodwracalna.", + "delete-ota-update-title":"Czy na pewno chcesz usunąć aktualizację OTA „{{title}}”?", + "delete-ota-updates-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane aktualizacje OTA zostaną usunięte.", + "delete-ota-updates-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 aktualizację OTA} other {# aktualizacji OTA} }?", + "description":"Opis", + "direct-url":"Bezpośredni adres URL", + "direct-url-copied-message":"Bezpośredni adres URL pakietu został skopiowany do schowka", + "direct-url-required":"Bezpośredni adres URL jest wymagany", + "download":"Pobierz pakiet", + "drop-file":"Przeciągnij i upuść plik pakietu lub kliknij, aby wybrać plik do przesłania.", + "drop-package-file-or":"Przeciągnij i upuść plik pakietu lub", + "file-name":"Nazwa pliku", + "file-size":"Rozmiar pliku", + "file-size-bytes":"Rozmiar pliku w bajtach", + "idCopiedMessage":"Identyfikator pakietu został skopiowany do schowka", + "no-firmware-matching":"Nie znaleziono kompatybilnych pakietów aktualizacji oprogramowania układowego pasujących do „{{entity}}”.", + "no-firmware-text":"Brak kompatybilnych pakietów aktualizacji oprogramowania układowego.", + "no-packages-text":"Nie znaleziono pakietów", + "no-software-matching":"Nie znaleziono kompatybilnych pakietów aktualizacji oprogramowania pasujących do „{{entity}}”.", + "no-software-text":"Brak kompatybilnych pakietów aktualizacji oprogramowania.", + "ota-update":"Aktualizacja OTA", + "ota-update-details":"Szczegóły aktualizacji OTA", + "ota-updates":"Aktualizacje OTA", + "package-file":"Plik pakietu", + "package-type":"Typ pakietu", + "packages-repository":"Repozytorium pakietów", + "search":"Szukaj pakietów", + "selected-package":"{ count, plural, =1 {1 pakiet} other {# pakietów} } wybrano", + "title":"Tytuł", + "title-required":"Tytuł jest wymagany.", + "title-max-length":"Tytuł powinien mieć mniej niż 256", + "types":{ + "firmware":"Oprogramowanie układowe", + "software":"Oprogramowanie" + }, + "upload-binary-file":"Prześlij plik binarny", + "use-external-url":"Użyj zewnętrznego adresu URL", + "version":"Wersja", + "version-required":"Wersja jest wymagana.", + "version-tag":"Tag wersji", + "version-tag-hint":"Niestandardowy tag powinien pasować do wersji pakietu zgłoszonej przez Twoje urządzenie.", + "version-max-length":"Wersja powinna mieć mniej niż 256", + "warning-after-save-no-edit":"Po przesłaniu pakietu nie będziesz mógł edytować tytułu, wersji, profilu urządzenia i typu pakietu." + }, + "position":{ + "top":"Góra", + "bottom":"Dół", + "left":"Lewo", + "right":"Prawo" + }, + "profile":{ + "profile":"Profil", + "last-login-time":"Ostatnie logowanie", + "change-password":"Zmień hasło", + "current-password":"Obecne hasło", + "copy-jwt-token":"Kopiuj token JWT", + "jwt-token":"Token JWT", + "token-valid-till":"Token jest ważny do", + "tokenCopiedSuccessMessage":"Token JWT został skopiowany do schowka", + "tokenCopiedWarnMessage":"Token JWT wygasł! Proszę odświeżyć stronę." + }, + "profiles":{ + "profiles":"Profile" + }, + "security":{ + "security":"Bezpieczeństwo", + "general-settings":"Ogólne ustawienia bezpieczeństwa", + "access-token":"Token dostępu", + "access-token-required":"Token dostępu jest wymagany", + "clientId":"ID klienta", + "clientId-required":"ID klienta jest wymagane", + "username":"Nazwa użytkownika", + "username-required":"Nazwa użytkownika jest wymagana", + "ca-cert":"Certyfikat CA", + "2fa":{ + "2fa":"Uwierzytelnianie dwuskładnikowe", + "2fa-description":"Uwierzytelnianie dwuskładnikowe chroni Twoje konto przed nieautoryzowanym dostępem. Wystarczy wprowadzić kod zabezpieczeń podczas logowania.", + "authenticate-with":"Możesz uwierzytelniać się za pomocą:", + "disable-2fa-provider-text":"Wyłączenie {{name}} spowoduje obniżenie poziomu bezpieczeństwa Twojego konta", + "disable-2fa-provider-title":"Czy na pewno chcesz wyłączyć {{name}}?", + "get-new-code":"Pobierz nowy kod", + "main-2fa-method":"Użyj jako główna metoda uwierzytelniania dwuskładnikowego", + "dialog":{ + "activation-step-description-email":"Następnym razem, gdy się zalogujesz, zostaniesz poproszony o wprowadzenie kodu zabezpieczeń, który zostanie wysłany na Twój adres e-mail.", + "activation-step-description-sms":"Następnym razem, gdy się zalogujesz, zostaniesz poproszony o wprowadzenie kodu zabezpieczeń, który zostanie wysłany na numer telefonu.", + "activation-step-description-totp":"Następnym razem, gdy się zalogujesz, będziesz musiał wprowadzić kod uwierzytelniania dwuskładnikowego.", + "activation-step-label":"Aktywacja", + "backup-code-description":"Wydrukuj kody, aby mieć je pod ręką, gdy będziesz musiał ich użyć do zalogowania się na swoje konto. Każdy kod zapasowy można użyć tylko raz.", + "backup-code-warn":"Po opuszczeniu tej strony te kody nie będą już dostępne. Przechowuj je bezpiecznie, korzystając z dostępnych opcji poniżej.", + "download-txt":"Pobierz (txt)", + "email-step-description":"Podaj adres e-mail, który będziesz używać jako autentykatora.", + "email-step-label":"E-mail", + "enable-email-title":"Włącz autentykator e-mail", + "enable-sms-title":"Włącz autentykator SMS", + "enable-totp-title":"Włącz autentykator aplikacji", + "enter-verification-code":"Wprowadź tutaj 6-cyfrowy kod", + "get-backup-code-title":"Pobierz kod zapasowy", + "next":"Dalej", + "scan-qr-code":"Zeskanuj ten kod QR za pomocą swojej aplikacji uwierzytelniającej", + "send-code":"Wyślij kod", + "sms-step-description":"Podaj numer telefonu, który będziesz używać jako autentykatora.", + "sms-step-label":"Numer telefonu", + "success":"Sukces!", + "totp-step-description-install":"Możesz zainstalować aplikacje takie jak Google Authenticator, Authy lub Duo.", + "totp-step-description-open":"Otwórz aplikację autentykatora na swoim telefonie komórkowym.", + "totp-step-label":"Pobierz aplikację", + "verification-code":"Kod weryfikacyjny 6-cyfrowy", + "verification-code-invalid":"Nieprawidłowy format kodu weryfikacyjnego", + "verification-code-incorrect":"Kod weryfikacyjny jest niepoprawny", + "verification-code-many-request":"Zbyt wiele żądań sprawdzenia kodu weryfikacyjnego", + "verification-step-description":"Wprowadź 6-cyfrowy kod, który właśnie wysłaliśmy na adres {{address}}", + "verification-step-label":"Weryfikacja" + }, + "provider":{ + "email":"E-mail", + "email-description":"Użyj kodu zabezpieczeń wysłanego na Twój adres e-mail do uwierzytelniania.", + "email-hint":"Kody uwierzytelniania są wysyłane przez e-mail na adres {{ info }}", + "sms":"SMS", + "sms-description":"Użyj telefonu do uwierzytelniania. Wyślemy Ci kod zabezpieczeń za pomocą wiadomości SMS podczas logowania.", + "sms-hint":"Kody uwierzytelniania są wysyłane jako wiadomości tekstowe na numer telefonu {{ info }}", + "totp":"Aplikacja autentykatora", + "totp-description":"Używaj aplikacji takich jak Google Authenticator, Authy lub Duo na swoim telefonie do uwierzytelniania. Wygeneruje ona kod zabezpieczeń do logowania.", + "totp-hint":"Aplikacja autentykatora jest skonfigurowana dla Twojego konta", + "backup_code":"Kod zapasowy", + "backup-code-description":"Te jednorazowe kody do wydruku pozwalają Ci się zalogować, gdy jesteś z dala od telefonu, na przykład podczas podróży.", + "backup-code-hint":"Obecnie aktywnych jest {{ info }} jednorazowych kodów" + } + }, + "password-requirement":{ + "at-least":"Przynajmniej:", + "character":"{ count, plural, =1 {1 znak} other {# znaków} }", + "digit":"{ count, plural, =1 {1 cyfra} other {# cyfr} }", + "incorrect-password-try-again":"Nieprawidłowe hasło. Spróbuj ponownie", + "lowercase-letter":"{ count, plural, =1 {1 mała litera} other {# małe litery} }", + "new-passwords-not-match":"Nowe hasło nie pasuje", + "password-should-not-contain-spaces":"Twoje hasło nie powinno zawierać spacji", + "password-not-meet-requirements":"Hasło nie spełnia wymagań", + "password-requirements":"Wymagania dotyczące hasła", + "password-should-difference":"Nowe hasło powinno różnić się od bieżącego", + "special-character":"{ count, plural, =1 {1 znak specjalny} other {# znaków specjalnych} }", + "uppercase-letter":"{ count, plural, =1 {1 wielka litera} other {# wielkie litery} }", + "at-most":"Maksymalnie:" + } + }, + "relation":{ + "relations":"Relacje", + "direction":"Kierunek", + "clear-relation-type":"Wyczyść typ relacji", + "search-direction":{ + "FROM":"Od", + "TO":"Do" + }, + "direction-type":{ + "FROM":"from", + "TO":"to" + }, + "from-relations":"Wychodzące relacje", + "to-relations":"Przychodzące relacje", + "selected-relations":"{ count, plural, =1 {1 relacja} other {# relacji} } wybrano", + "type":"Typ", + "to-entity-type":"Typ jednostki docelowej", + "to-entity-name":"Nazwa jednostki docelowej", + "from-entity-type":"Typ jednostki źródłowej", + "from-entity-name":"Nazwa jednostki źródłowej", + "to-entity":"Jednostka docelowa", + "from-entity":"Jednostka źródłowa", + "delete":"Usuń relację", + "relation-type":"Typ relacji", + "relation-type-required":"Wymagany jest typ relacji.", + "relation-type-max-length":"Typ relacji powinien mieć mniej niż 256 znaków", + "any-relation-type":"Dowolny typ", + "add":"Dodaj relację", + "edit":"Edytuj relację", + "delete-to-relation-title":"Czy na pewno chcesz usunąć relację do jednostki '{{entityName}}'?", + "delete-to-relation-text":"Bądź ostrożny, po potwierdzeniu jednostka '{{entityName}}' zostanie odłączona od bieżącej jednostki.", + "delete-to-relations-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 relacja} other {# relacje} }?", + "delete-to-relations-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane relacje zostaną usunięte, a odpowiednie jednostki zostaną odłączone od bieżącej jednostki.", + "delete-from-relation-title":"Czy na pewno chcesz usunąć relację z jednostki '{{entityName}}'?", + "delete-from-relation-text":"Bądź ostrożny, po potwierdzeniu bieżąca jednostka zostanie odłączona od jednostki '{{entityName}}'.", + "delete-from-relations-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 relacja} other {# relacje} }?", + "delete-from-relations-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane relacje zostaną usunięte, a bieżąca jednostka zostanie odłączona od odpowiednich jednostek.", + "remove-relation-filter":"Usuń filtr relacji", + "remove-filter":"Usuń filtr", + "add-relation-filter":"Dodaj filtr relacji", + "any-relation":"Dowolna relacja", + "relation-filters":"Filtry relacji", + "additional-info":"Dodatkowe informacje (JSON)", + "invalid-additional-info":"Nie można sparsować dodatkowych informacji json.", + "no-relations-text":"Nie znaleziono relacji" + }, + "resource":{ + "add":"Dodaj zasób", + "all-types":"Wszystkie", + "copyId":"Skopiuj identyfikator zasobu", + "delete":"Usuń zasób", + "delete-resource-text":"Bądź ostrożny, po potwierdzeniu zasób zostanie bezpowrotnie usunięty.", + "delete-resource-title":"Czy na pewno chcesz usunąć zasób '{{resourceTitle}}'?", + "delete-resources-action-title":"Usuń { count, plural, =1 {1 zasób} other {# zasobów} }", + "delete-resources-text":"Proszę zauważyć, że wybrane zasoby, nawet jeśli są używane w profilach urządzeń, zostaną usunięte.", + "delete-resources-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 zasób} other {# zasobów} }?", + "download":"Pobierz zasób", + "drop-file":"Upuść plik zasobu lub kliknij, aby wybrać plik do przesłania.", + "drop-resource-file-or":"Przeciągnij i upuść plik zasobu lub", + "empty":"Zasób jest pusty", + "file-name":"Nazwa pliku", + "idCopiedMessage":"Identyfikator zasobu został skopiowany do schowka", + "no-resource-matching":"Nie znaleziono zasobów pasujących do '{{widgetsBundle}}'.", + "no-resource-text":"Nie znaleziono zasobów", + "open-widgets-bundle":"Otwórz paczkę widżetów", + "resource":"Zasób", + "resource-file":"Plik zasobu", + "resource-files":"Pliki zasobów", + "resource-library-details":"Szczegóły zasobu", + "resource-type":"Typ zasobu", + "resources-library":"Biblioteka zasobów", + "search":"Szukaj zasobów", + "selected-resources":"{ count, plural, =1 {1 zasób} other {# zasobów} } wybrano", + "system":"System", + "title":"Tytuł", + "title-required":"Tytuł jest wymagany.", + "title-max-length":"Tytuł powinien mieć mniej niż 256 znaków", + "type":{ + "jks":"JKS", + "js-module":"Moduł JS", + "lwm2m-model":"Model LWM2M", + "pkcs-12":"PKCS #12" + } + }, + "rulechain":{ + "rulechain":"Łańcuch reguł", + "rulechain-events":"Zdarzenia łańcucha reguł", + "rulechains":"Łańcuchy reguł", + "root":"Korzeń", + "delete":"Usuń łańcuch reguł", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "name-max-length":"Nazwa powinna mieć mniej niż 256 znaków", + "description":"Opis", + "add":"Dodaj łańcuch reguł", + "set-root":"Ustaw łańcuch reguł jako korzeń", + "set-root-rulechain-title":"Jesteś pewien, że chcesz ustawić łańcuch reguł '{{ruleChainName}}' jako korzeń?", + "set-root-rulechain-text":"Po potwierdzeniu łańcuch reguł stanie się korzeniem i będzie obsługiwał wszystkie przychodzące wiadomości transportowe.", + "delete-rulechain-title":"Jesteś pewien, że chcesz usunąć łańcuch reguł '{{ruleChainName}}'?", + "delete-rulechain-text":"Bądź ostrożny, po potwierdzeniu łańcuch reguł i wszystkie powiązane dane staną się nieodwracalne.", + "delete-rulechains-title":"Jesteś pewien, że chcesz usunąć { count, plural, =1 {1 łańcuch reguł} other {# łańcuchów reguł} }?", + "delete-rulechains-action-title":"Usuń { count, plural, =1 {1 łańcuch reguł} other {# łańcuchów reguł} }", + "delete-rulechains-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane łańcuchy reguł zostaną usunięte, a wszystkie powiązane dane staną się nieodwracalne.", + "add-rulechain-text":"Dodaj nowy łańcuch reguł", + "no-rulechains-text":"Nie znaleziono łańcuchów reguł", + "rulechain-details":"Szczegóły łańcucha reguł", + "details":"Szczegóły", + "events":"Zdarzenia", + "system":"System", + "import":"Importuj łańcuch reguł", + "export":"Eksportuj łańcuch reguł", + "export-failed-error":"Nie można wyeksportować łańcucha reguł: {{error}}", + "create-new-rulechain":"Utwórz nowy łańcuch reguł", + "rulechain-file":"Plik łańcucha reguł", + "invalid-rulechain-file-error":"Nie można zaimportować łańcucha reguł: Nieprawidłowa struktura danych łańcucha reguł.", + "copyId":"Skopiuj identyfikator łańcucha reguł", + "idCopiedMessage":"Identyfikator łańcucha reguł został skopiowany do schowka", + "select-rulechain":"Wybierz łańcuch reguł", + "no-rulechains-matching":"Nie znaleziono łańcuchów reguł pasujących do '{{entity}}'.", + "rulechain-required":"Łańcuch reguł jest wymagany", + "management":"Zarządzanie regułami", + "debug-mode":"Tryb debugowania", + "search":"Wyszukaj łańcuchy reguł", + "selected-rulechains":"{ count, plural, =1 {1 łańcuch reguł} other {# łańcuchów reguł} } wybranych", + "open-rulechain":"Otwórz łańcuch reguł", + "edge-template-root":"Korzeń szablonu", + "assign-to-edge":"Przypisz do krawędzi", + "edge-rulechain":"Łańcuch reguł krawędzi", + "unassign-rulechain-from-edge-text":"Po potwierdzeniu łańcuch reguł zostanie odłączony i nie będzie dostępny dla krawędzi.", + "unassign-rulechains-from-edge-title":"Jesteś pewien, że chcesz odłączyć { count, plural, =1 {1 łańcuch reguł} other {# łańcuchów reguł} }?", + "unassign-rulechains-from-edge-text":"Po potwierdzeniu wszystkie wybrane łańcuchy reguł zostaną odłączone i nie będą dostępne dla krawędzi.", + "assign-rulechain-to-edge-title":"Przypisz łańcuch(y) reguł do krawędzi", + "assign-rulechain-to-edge-text":"Wybierz łańcuchy reguł do przypisania do krawędzi", + "set-edge-template-root-rulechain":"Ustaw łańcuch reguł jako korzeń szablonu krawędzi", + "set-edge-template-root-rulechain-title":"Jesteś pewien, że chcesz ustawić łańcuch reguł '{{ruleChainName}}' jako korzeń szablonu krawędzi?", + "set-edge-template-root-rulechain-text":"Po potwierdzeniu łańcuch reguł stanie się korzeniem szablonu krawędzi i będzie korzeniem łańcucha reguł dla nowo tworzonych krawędzi.", + "invalid-rulechain-type-error":"Nie można zaimportować łańcucha reguł: Nieprawidłowy typ łańcucha reguł. Oczekiwany typ to {{expectedRuleChainType}}.", + "set-auto-assign-to-edge":"Przypisz łańcuch reguł do krawędzi podczas tworzenia", + "set-auto-assign-to-edge-title":"Jesteś pewien, że chcesz przypisać łańcuch reguł krawędzi '{{ruleChainName}}' do krawędzi podczas tworzenia?", + "set-auto-assign-to-edge-text":"Po potwierdzeniu łańcuch reguł krawędzi zostanie automatycznie przypisany do krawędzi podczas tworzenia.", + "unset-auto-assign-to-edge":"Nie przypisuj łańcucha reguł do krawędzi podczas tworzenia", + "unset-auto-assign-to-edge-title":"Jesteś pewien, że nie chcesz przypisywać łańcucha reguł krawędzi '{{ruleChainName}}' do krawędzi podczas tworzenia?", + "unset-auto-assign-to-edge-text":"Po potwierdzeniu łańcuch reguł krawędzi nie będzie automatycznie przypisywany do krawędzi podczas tworzenia.", + "unassign-rulechain-title":"Jesteś pewien, że chcesz odłączyć łańcuch reguł '{{ruleChainName}}'?", + "unassign-rulechains":"Odłącz łańcuchy reguł" + }, + "rulenode":{ + "rule-node-events":"Zdarzenia węzła reguły", + "details":"Szczegóły", + "events":"Zdarzenia", + "search":"Wyszukaj węzły", + "open-node-library":"Otwórz bibliotekę węzłów", + "close-node-library":"Zamknij bibliotekę węzłów", + "add":"Dodaj węzeł reguły", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "name-max-length":"Nazwa powinna mieć mniej niż 256 znaków", + "type":"Typ", + "rule-node-description":"Opis węzła reguły", + "delete":"Usuń węzeł reguły", + "select-all-objects":"Zaznacz wszystkie węzły i połączenia", + "deselect-all-objects":"Odznacz wszystkie węzły i połączenia", + "delete-selected-objects":"Usuń wybrane węzły i połączenia", + "delete-selected":"Usuń zaznaczone", + "create-nested-rulechain":"Utwórz zagnieżdżony łańcuch reguł", + "select-all":"Zaznacz wszystkie", + "copy-selected":"Skopiuj zaznaczone", + "deselect-all":"Odznacz wszystkie", + "rulenode-details":"Szczegóły węzła reguły", + "debug-mode":"Tryb debugowania", + "singleton-mode":"Tryb pojedynczej instancji", + "configuration":"Konfiguracja", + "link":"Połączenie", + "link-details":"Szczegóły połączenia węzła reguły", + "add-link":"Dodaj połączenie", + "link-label":"Etykieta połączenia", + "link-label-required":"Etykieta połączenia jest wymagana.", + "custom-link-label":"Niestandardowa etykieta połączenia", + "custom-link-label-required":"Niestandardowa etykieta połączenia jest wymagana.", + "link-labels":"Etykiety połączeń", + "link-labels-required":"Wymagane są etykiety połączeń.", + "no-link-labels-found":"Nie znaleziono etykiet połączeń", + "no-link-label-matching":"'{{label}}' nie zostało znalezione.", + "create-new-link-label":"Utwórz nową!", + "type-filter":"Filtr", + "type-filter-details":"Filtruj przychodzące wiadomości z określonymi warunkami", + "type-enrichment":"Wzbogacenie", + "type-enrichment-details":"Dodaj dodatkowe informacje do metadanych wiadomości", + "type-transformation":"Transformacja", + "type-transformation-details":"Zmień ładunek wiadomości i metadane", + "type-action":"Akcja", + "type-action-details":"Wykonaj specjalną akcję", + "type-external":"Zewnętrzne", + "type-external-details":"Komunikuje się z zewnętrznym systemem", + "type-rule-chain":"Łańcuch reguł", + "type-rule-chain-details":"Przekazuje przychodzące wiadomości do określonego łańcucha reguł", + "type-flow":"Przepływ", + "type-flow-details":"Organizuje przepływ wiadomości", + "type-input":"Wejście", + "type-input-details":"Logiczne wejście łańcucha reguł, przekazuje przychodzące wiadomości do następnego powiązanego węzła reguły", + "type-unknown":"Nieznany", + "type-unknown-details":"Nierozwiązany węzeł reguły", + "directive-is-not-loaded":"Zdefiniowana dyrektywa konfiguracji '{{directiveName}}' nie jest dostępna.", + "ui-resources-load-error":"Nie udało się załadować zasobów interfejsu użytkownika konfiguracji.", + "invalid-target-rulechain":"Nie można rozpoznać docelowego łańcucha reguł!", + "test-script-function":"Funkcja skryptu testowego", + "script-lang-java-script":"JavaScript", + "script-lang-tbel":"TBEL", + "message":"Wiadomość", + "message-type":"Typ wiadomości", + "select-message-type":"Wybierz typ wiadomości", + "message-type-required":"Wymagany jest typ wiadomości", + "metadata":"Metadane", + "metadata-required":"Wpisy metadanych nie mogą być puste.", + "output":"Wynik", + "test":"Test", + "help":"Pomoc", + "reset-debug-mode":"Zresetuj tryb debugowania we wszystkich węzłach", + "test-with-this-message":"{{test}} z tą wiadomością", + "queue-hint":"Wybierz kolejkę do przekazywania wiadomości do innej kolejki. Domyślnie używana jest kolejka „Main”.", + "queue-singleton-hint":"Wybierz kolejkę do przekazywania wiadomości w środowiskach wielu instancji. Domyślnie używana jest kolejka „Main”." + }, + "timezone":{ + "timezone":"Strefa czasowa", + "select-timezone":"Wybierz strefę czasową", + "no-timezones-matching":"Nie znaleziono stref czasowych pasujących do '{{timezone}}'.", + "timezone-required":"Strefa czasowa jest wymagana.", + "browser-time":"Czas przeglądarki" + }, + "queue":{ + "queue-name":"Kolejka", + "no-queues-found":"Nie znaleziono kolejek.", + "no-queues-matching":"Nie znaleziono kolejek pasujących do '{{queue}}'.", + "select-name":"Wybierz nazwę kolejki", + "name":"Nazwa", + "name-required":"Nazwa kolejki jest wymagana!", + "name-unique":"Nazwa kolejki nie jest unikalna!", + "name-pattern":"Nazwa kolejki zawiera znak inny niż alfanumeryczny ASCII, '.', '_', i '-'!", + "queue-required":"Kolejka jest wymagana!", + "topic-required":"Temat kolejki jest wymagany!", + "poll-interval-required":"Interwał odpytywania jest wymagany!", + "poll-interval-min-value":"Wartość interwału odpytywania nie może być mniejsza niż 1", + "partitions-required":"Partycje są wymagane!", + "partitions-min-value":"Wartość partycji nie może być mniejsza niż 1", + "pack-processing-timeout-required":"Limit czasu przetwarzania jest wymagany", + "pack-processing-timeout-min-value":"Wartość limitu czasu przetwarzania nie może być mniejsza niż 1", + "batch-size-required":"Rozmiar partii jest wymagany!", + "batch-size-min-value":"Wartość rozmiaru partii nie może być mniejsza niż 1", + "retries-required":"Liczba prób jest wymagana!", + "retries-min-value":"Wartość liczby prób nie może być ujemna", + "failure-percentage-required":"Procent awaryjnych wiadomości jest wymagany!", + "failure-percentage-min-value":"Wartość procenta awaryjnych wiadomości nie może być mniejsza niż 0", + "failure-percentage-max-value":"Wartość procenta awaryjnych wiadomości nie może być większa niż 100", + "pause-between-retries-required":"Odstęp między próbami jest wymagany!", + "pause-between-retries-min-value":"Wartość odstępu między próbami nie może być mniejsza niż 1", + "max-pause-between-retries-required":"Maksymalny odstęp między próbami jest wymagany!", + "max-pause-between-retries-min-value":"Wartość maksymalnego odstępu między próbami nie może być mniejsza niż 1", + "submit-strategy-type-required":"Typ strategii przesyłania jest wymagany!", + "processing-strategy-type-required":"Typ strategii przetwarzania jest wymagany!", + "queues":"Kolejki", + "selected-queues":"{ count, plural, =1 {1 kolejka} other {# kolejek} } wybranych", + "delete-queue-title":"Czy na pewno chcesz usunąć kolejkę '{{queueName}}'?", + "delete-queues-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 kolejkę} other {# kolejki} }?", + "delete-queue-text":"Bądź ostrożny, po potwierdzeniu kolejka i wszystkie związane z nią dane zostaną bezpowrotnie usunięte.", + "delete-queues-text":"Po potwierdzeniu wszystkie wybrane kolejki zostaną usunięte i nie będą dostępne.", + "search":"Wyszukaj kolejkę", + "add":"Dodaj kolejkę", + "details":"Szczegóły kolejki", + "topic":"Temat", + "submit-settings":"Ustawienia przesyłania", + "submit-strategy":"Typ strategii *", + "grouping-parameter":"Parametr grupowania", + "processing-settings":"Ustawienia przetwarzania", + "processing-strategy":"Typ przetwarzania *", + "retries-settings":"Ustawienia prób", + "polling-settings":"Ustawienia odpytywania", + "batch-processing":"Przetwarzanie partiami", + "poll-interval":"Interwał odpytywania", + "partitions":"Partycje", + "immediate-processing":"Natychmiastowe przetwarzanie", + "consumer-per-partition":"Wysyłaj odpytywanie dla każdego konsumenta", + "consumer-per-partition-hint":"Włącz oddzielnych konsumentów dla każdej partycji", + "processing-timeout":"Przetwarzaj w ciągu, ms", + "batch-size":"Rozmiar partii", + "retries":"Liczba prób (0 - nieograniczona)", + "failure-percentage":"Procent awaryjnych wiadomości do pominięcia prób", + "pause-between-retries":"Odstęp między próbami, s", + "max-pause-between-retries":"Maksymalny odstęp między próbami, s", + "delete":"Usuń kolejkę", + "copyId":"Skopiuj Id kolejki", + "idCopiedMessage":"Id kolejki zostało skopiowane do schowka", + "description":"Opis", + "description-hint":"Ten tekst będzie wyświetlany w opisie kolejki zamiast wybranej strategii", + "alt-description":"Strategia przesyłania: {{submitStrategy}}, Strategia przetwarzania: {{processingStrategy}}", + "custom-properties":"Własne właściwości", + "custom-properties-hint":"Własne właściwości tworzenia kolejki (tematu), np. 'retention.ms:604800000;retention.bytes:1048576000'", + "strategies":{ + "sequential-by-originator-label":"Sekwencyjne według nadawcy", + "sequential-by-originator-hint":"Nowa wiadomość, na przykład dla urządzenia A, nie jest przesyłana, dopóki poprzednia wiadomość dla urządzenia A nie zostanie potwierdzona", + "sequential-by-tenant-label":"Sekwencyjne według najemcy", + "sequential-by-tenant-hint":"Nowa wiadomość, na przykład dla najemcy A, nie jest przesyłana, dopóki poprzednia wiadomość dla najemcy A nie zostanie potwierdzona", + "sequential-label":"Sekwencyjne", + "sequential-hint":"Nowa wiadomość nie jest przesyłana, dopóki poprzednia wiadomość nie zostanie potwierdzona", + "burst-label":"Burst", + "burst-hint":"Wszystkie wiadomości są przesyłane do łańcuchów reguł w kolejności ich przyjścia", + "batch-label":"Partia", + "batch-hint":"Nowa partia nie jest przesyłana, dopóki poprzednia partia nie zostanie potwierdzona", + "skip-all-failures-label":"Pomiń wszystkie awarie", + "skip-all-failures-hint":"Ignoruj wszystkie awarie", + "skip-all-failures-and-timeouts-label":"Pomiń wszystkie awarie i przerwy czasowe", + "skip-all-failures-and-timeouts-hint":"Ignoruj wszystkie awarie i przerwy czasowe", + "retry-all-label":"Ponów wszystkie", + "retry-all-hint":"Ponów wszystkie wiadomości z pakietu przetwarzania", + "retry-failed-label":"Ponów nieudane", + "retry-failed-hint":"Ponów wszystkie nieudane wiadomości z pakietu przetwarzania", + "retry-timeout-label":"Ponów przerwę czasową", + "retry-timeout-hint":"Ponów wszystkie wiadomości przekroczone czasem z pakietu przetwarzania", + "retry-failed-and-timeout-label":"Ponów nieudane i przerwę czasową", + "retry-failed-and-timeout-hint":"Ponów wszystkie nieudane i przekroczone czasem wiadomości z pakietu przetwarzania" + } + }, + "server-error":{ + "general":"Ogólny błąd serwera", + "authentication":"Błąd uwierzytelniania", + "jwt-token-expired":"Token JWT wygasł", + "tenant-trial-expired":"Próba najemcy wygasła", + "credentials-expired":"Wygasły poświadczenia", + "permission-denied":"Brak uprawnień", + "invalid-arguments":"Nieprawidłowe argumenty", + "bad-request-params":"Nieprawidłowe parametry żądania", + "item-not-found":"Nie znaleziono elementu", + "too-many-requests":"Zbyt wiele żądań", + "too-many-updates":"Zbyt wiele aktualizacji" + }, + "tenant":{ + "tenant":"Najemca", + "tenants":"Najemcy", + "management":"Zarządzanie najemcami", + "add":"Dodaj najemcę", + "admins":"Administratorzy", + "manage-tenant-admins":"Zarządzaj administratorami najemcy", + "delete":"Usuń najemcę", + "add-tenant-text":"Dodaj nowego najemcę", + "no-tenants-text":"Nie znaleziono najemców", + "tenant-details":"Szczegóły najemcy", + "title-max-length":"Tytuł powinien zawierać mniej niż 256 znaków", + "delete-tenant-title":"Czy na pewno chcesz usunąć najemcę '{{tenantTitle}}'?", + "delete-tenant-text":"Bądź ostrożny, po potwierdzeniu najemca i wszystkie związane z nim dane zostaną bezpowrotnie usunięte.", + "delete-tenants-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 najemcę} other {# najemców} }?", + "delete-tenants-action-title":"Usuń { count, plural, =1 {1 najemcę} other {# najemców} }", + "delete-tenants-text":"Bądź ostrożny, po potwierdzeniu wszyscy wybrani najemcy zostaną usunięci, a wszystkie związane z nimi dane zostaną bezpowrotnie usunięte.", + "title":"Tytuł", + "title-required":"Tytuł jest wymagany.", + "description":"Opis", + "details":"Szczegóły", + "events":"Zdarzenia", + "copyId":"Kopiuj Id najemcy", + "idCopiedMessage":"Id najemcy zostało skopiowane do schowka", + "select-tenant":"Wybierz najemcę", + "no-tenants-matching":"Nie znaleziono najemców pasujących do '{{entity}}'", + "tenant-required":"Najemca jest wymagany", + "search":"Szukaj najemców", + "selected-tenants":"{ count, plural, =1 {1 najemca} other {# najemcy} } wybrano", + "isolated-tb-rule-engine":"Użyj izolowanych kolejek ThingsBoard Rule Engine", + "isolated-tb-rule-engine-details":"Każdy najemca będzie miał dedykowane kolejki Rule Engine" + }, + "tenant-profile":{ + "tenant-profile":"Profil najemcy", + "tenant-profiles":"Profile najemców", + "add":"Dodaj profil najemcy", + "add-profile":"Dodaj profil", + "edit":"Edytuj profil najemcy", + "tenant-profile-details":"Szczegóły profilu najemcy", + "no-tenant-profiles-text":"Nie znaleziono profili najemców", + "name-max-length":"Nazwa powinna zawierać mniej niż 256 znaków", + "search":"Szukaj profili najemców", + "selected-tenant-profiles":"{ count, plural, =1 {1 profil najemcy} other {# profile najemców} } wybrano", + "no-tenant-profiles-matching":"Nie znaleziono profili najemców pasujących do '{{entity}}'", + "tenant-profile-required":"Profil najemcy jest wymagany", + "idCopiedMessage":"Id profilu najemcy zostało skopiowane do schowka", + "set-default":"Ustaw profil najemcy jako domyślny", + "delete":"Usuń profil najemcy", + "copyId":"Kopiuj Id profilu najemcy", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "data":"Dane profilu", + "profile-configuration":"Konfiguracja profilu", + "description":"Opis", + "default":"Domyślny", + "delete-tenant-profile-title":"Czy na pewno chcesz usunąć profil najemcy '{{tenantProfileName}}'?", + "delete-tenant-profile-text":"Bądź ostrożny, po potwierdzeniu profil najemcy i wszystkie związane z nim dane zostaną bezpowrotnie usunięte.", + "delete-tenant-profiles-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 profil najemcy} other {# profile najemców} }?", + "delete-tenant-profiles-text":"Bądź ostrożny, po potwierdzeniu wszystkie wybrane profile najemców zostaną usunięte, a wszystkie związane z nimi dane zostaną bezpowrotnie usunięte.", + "set-default-tenant-profile-title":"Czy na pewno chcesz ustawić profil najemcy '{{tenantProfileName}}' jako domyślny?", + "set-default-tenant-profile-text":"Po potwierdzeniu profil najemcy zostanie oznaczony jako domyślny i będzie używany dla nowych najemców bez określonego profilu.", + "no-tenant-profiles-found":"Nie znaleziono profili najemców.", + "create-new-tenant-profile":"Utwórz nowy!", + "create-tenant-profile":"Utwórz nowy profil najemcy", + "import":"Importuj profil najemcy", + "export":"Eksportuj profil najemcy", + "export-failed-error":"Nie można wyeksportować profilu najemcy: {{error}}", + "tenant-profile-file":"Plik profilu najemcy", + "invalid-tenant-profile-file-error":"Nie można zaimportować profilu najemcy: Nieprawidłowa struktura danych profilu najemcy.", + "advanced-settings":"Ustawienia zaawansowane", + "entities":"Encje", + "rule-engine":"Silnik reguł", + "time-to-live":"Czas życia", + "alarms-and-notifications":"Alarmy i powiadomienia", + "ota-files-in-bytes":"Pliki", + "ws-title":"WS", + "unlimited":"(0 - nielimitowany)", + "maximum-devices":"Maksymalna liczba urządzeń", + "maximum-devices-required":"Maksymalna liczba urządzeń jest wymagana.", + "maximum-devices-range":"Maksymalna liczba urządzeń nie może być ujemna", + "maximum-assets":"Maksymalna liczba zasobów", + "maximum-assets-required":"Maksymalna liczba zasobów jest wymagana.", + "maximum-assets-range":"Maksymalna liczba zasobów nie może być ujemna", + "maximum-customers":"Maksymalna liczba klientów", + "maximum-customers-required":"Maksymalna liczba klientów jest wymagana.", + "maximum-customers-range":"Maksymalna liczba klientów nie może być ujemna", + "maximum-users":"Maksymalna liczba użytkowników", + "maximum-users-required":"Maksymalna liczba użytkowników jest wymagana.", + "maximum-users-range":"Maksymalna liczba użytkowników nie może być ujemna", + "maximum-dashboards":"Maksymalna liczba desek rozdzielczych", + "maximum-dashboards-required":"Maksymalna liczba desek rozdzielczych jest wymagana.", + "maximum-dashboards-range":"Maksymalna liczba desek rozdzielczych nie może być ujemna", + "maximum-rule-chains":"Maksymalna liczba łańcuchów reguł", + "maximum-rule-chains-required":"Maksymalna liczba łańcuchów reguł jest wymagana.", + "maximum-rule-chains-range":"Maksymalna liczba łańcuchów reguł nie może być ujemna", + "maximum-resources-sum-data-size":"Maksymalny łączny rozmiar plików zasobów (bajty)", + "maximum-resources-sum-data-size-required":"Maksymalny łączny rozmiar plików zasobów jest wymagany.", + "maximum-resources-sum-data-size-range":"Maksymalny łączny rozmiar plików zasobów nie może być ujemny", + "maximum-resource-size":"Maksymalny rozmiar pliku zasobu (bajty)", + "maximum-resource-size-required":"Maksymalny rozmiar pliku zasobu jest wymagany", + "maximum-resource-size-range":"Maksymalny rozmiar pliku zasobu nie może być ujemny", + "maximum-ota-packages-sum-data-size":"Maksymalny łączny rozmiar plików pakietów OTA (bajty)", + "maximum-ota-package-sum-data-size-required":"Maksymalny łączny rozmiar plików pakietów OTA jest wymagany.", + "maximum-ota-package-sum-data-size-range":"Maksymalny łączny rozmiar plików pakietów OTA nie może być ujemny", + "rest-requests-for-tenant":"Zapytania REST dla najemcy", + "transport-tenant-telemetry-msg-rate-limit":"Limit wiadomości telemetrii najemcy", + "transport-tenant-telemetry-data-points-rate-limit":"Limit punktów danych telemetrii najemcy", + "transport-device-msg-rate-limit":"Limit wiadomości urządzenia", + "transport-device-telemetry-msg-rate-limit":"Limit wiadomości telemetrii urządzenia", + "transport-device-telemetry-data-points-rate-limit":"Limit punktów danych telemetrii urządzenia", + "tenant-entity-export-rate-limit":"Limit eksportu wersji encji", + "tenant-entity-import-rate-limit":"Limit ładowania wersji encji", + "tenant-notification-request-rate-limit":"Limit żądań powiadomień", + "tenant-notification-requests-per-rule-rate-limit":"Limit żądań powiadomień na regułę powiadomień", + "max-transport-messages":"Maksymalna liczba wiadomości transportowych", + "max-transport-messages-required":"Maksymalna liczba wiadomości transportowych jest wymagana.", + "max-transport-messages-range":"Maksymalna liczba wiadomości transportowych nie może być ujemna", + "max-transport-data-points":"Maksymalna liczba punktów danych transportowych", + "max-transport-data-points-required":"Maksymalna liczba punktów danych transportowych jest wymagana.", + "max-transport-data-points-range":"Maksymalna liczba punktów danych transportowych nie może być ujemna", + "max-r-e-executions":"Maksymalna liczba wykonanych operacji Silnika Reguł", + "max-r-e-executions-required":"Maksymalna liczba wykonanych operacji Silnika Reguł jest wymagana.", + "max-r-e-executions-range":"Maksymalna liczba wykonanych operacji Silnika Reguł nie może być ujemna", + "max-j-s-executions":"Maksymalna liczba wykonanych operacji JavaScript", + "max-j-s-executions-required":"Maksymalna liczba wykonanych operacji JavaScript jest wymagana.", + "max-j-s-executions-range":"Maksymalna liczba wykonanych operacji JavaScript nie może być ujemna", + "max-tbel-executions":"Maksymalna liczba wykonanych operacji TBEL", + "max-tbel-executions-required":"Maksymalna liczba wykonanych operacji TBEL jest wymagana.", + "max-tbel-executions-range":"Maksymalna liczba wykonanych operacji TBEL nie może być ujemna", + "max-d-p-storage-days":"Maksymalna liczba dni przechowywania punktów danych", + "max-d-p-storage-days-required":"Maksymalna liczba dni przechowywania punktów danych jest wymagana.", + "max-d-p-storage-days-range":"Maksymalna liczba dni przechowywania punktów danych nie może być ujemna", + "default-storage-ttl-days":"Domyślny czas życia pamięci masowej w dniach", + "default-storage-ttl-days-required":"Domyślny czas życia pamięci masowej w dniach jest wymagany.", + "default-storage-ttl-days-range":"Domyślny czas życia pamięci masowej w dniach nie może być ujemny", + "alarms-ttl-days":"Czas życia alarmów w dniach", + "alarms-ttl-days-required":"Czas życia alarmów jest wymagany", + "alarms-ttl-days-days-range":"Okres przechowywania alarmów (TTL) nie może być ujemny", + "rpc-ttl-days":"RPC Okres przechowywania (TTL) dni", + "rpc-ttl-days-required":"Wymagany okres przechowywania (TTL) dni dla RPC", + "rpc-ttl-days-days-range":"Okres przechowywania (TTL) dni dla RPC nie może być ujemny", + "queue-stats-ttl-days":"Okres przechowywania (TTL) dni statystyk kolejki", + "queue-stats-ttl-days-required":"Wymagany okres przechowywania (TTL) dni dla statystyk kolejki", + "queue-stats-ttl-days-range":"Okres przechowywania (TTL) dni dla statystyk kolejki nie może być ujemny", + "rule-engine-exceptions-ttl-days":"Okres przechowywania (TTL) dni wyjątków silnika reguł", + "rule-engine-exceptions-ttl-days-required":"Wymagany okres przechowywania (TTL) dni dla wyjątków silnika reguł", + "rule-engine-exceptions-ttl-days-range":"Okres przechowywania (TTL) dni dla wyjątków silnika reguł nie może być ujemny", + "max-rule-node-executions-per-message":"Maksymalna liczba wykonanych węzłów reguł na wiadomość", + "max-rule-node-executions-per-message-required":"Wymagana maksymalna liczba wykonanych węzłów reguł na wiadomość", + "max-rule-node-executions-per-message-range":"Maksymalna liczba wykonanych węzłów reguł na wiadomość nie może być ujemna", + "max-emails":"Maksymalna liczba wysłanych wiadomości e-mail", + "max-emails-required":"Wymagana maksymalna liczba wysłanych wiadomości e-mail", + "max-emails-range":"Maksymalna liczba wysłanych wiadomości e-mail nie może być ujemna", + "sms-enabled":"SMS włączony", + "max-sms":"Maksymalna liczba wysłanych wiadomości SMS", + "max-sms-required":"Wymagana maksymalna liczba wysłanych wiadomości SMS", + "max-sms-range":"Maksymalna liczba wysłanych wiadomości SMS nie może być ujemna", + "max-created-alarms":"Maksymalna liczba utworzonych alarmów", + "max-created-alarms-required":"Wymagana maksymalna liczba utworzonych alarmów", + "max-created-alarms-range":"Maksymalna liczba utworzonych alarmów nie może być ujemna", + "no-queue":"Brak skonfigurowanej kolejki", + "add-queue":"Dodaj kolejkę", + "queues-with-count":"Kolejki ({{count}})", + "tenant-rest-limits":"Żądania REST dla najemcy", + "customer-rest-limits":"Żądania REST dla klienta", + "incorrect-pattern-for-rate-limits":"Nieprawidłowy format dla limitów szybkości. Pary pojemność:okres (w sekundach) oddzielone przecinkiem, np. 100:1,2000:60", + "too-small-value-zero":"Wartość musi być większa niż 0", + "too-small-value-one":"Wartość musi być większa niż 1", + "queue-size-is-limited-by-system-configuration":"Rozmiar kolejki jest również ograniczony konfiguracją systemu.", + "cassandra-tenant-limits-configuration":"Zapytanie do Cassandry dla najemcy", + "ws-limit-max-sessions-per-tenant":"Maksymalna liczba sesji na najemcę", + "ws-limit-max-sessions-per-customer":"Maksymalna liczba sesji na klienta", + "ws-limit-max-sessions-per-regular-user":"Maksymalna liczba sesji na zwykłego użytkownika", + "ws-limit-max-sessions-per-public-user":"Maksymalna liczba sesji na publicznego użytkownika", + "ws-limit-queue-per-session":"Maksymalny rozmiar kolejki wiadomości na sesję", + "ws-limit-max-subscriptions-per-tenant":"Maksymalna liczba subskrypcji na najemcę", + "ws-limit-max-subscriptions-per-customer":"Maksymalna liczba subskrypcji na klienta", + "ws-limit-max-subscriptions-per-regular-user":"Maksymalna liczba subskrypcji na zwykłego użytkownika", + "ws-limit-max-subscriptions-per-public-user":"Maksymalna liczba subskrypcji na publicznego użytkownika", + "ws-limit-updates-per-session":"Aktualizacje WS na sesję", + "rate-limits":{ + "add-limit":"Dodaj limit", + "advanced-settings":"Zaawansowane ustawienia", + "edit-limit":"Edytuj limit", + "but-less-than":"ale mniejsze niż", + "edit-transport-tenant-msg-title":"Edytuj limity szybkości przesyłania komunikatów najemcy", + "edit-transport-tenant-telemetry-msg-title":"Edytuj limity szybkości przesyłania telemetrii komunikatów najemcy", + "edit-transport-tenant-telemetry-data-points-title":"Edytuj limity szybkości przesyłania punktów danych telemetrii najemcy", + "edit-transport-device-msg-title":"Edytuj limity szybkości przesyłania komunikatów urządzenia", + "edit-transport-device-telemetry-msg-title":"Edytuj limity szybkości przesyłania telemetrii komunikatów urządzenia", + "edit-transport-device-telemetry-data-points-title":"Edytuj limity szybkości przesyłania punktów danych telemetrii urządzenia", + "edit-tenant-rest-limits-title":"Edytuj limity szybkości żądań REST dla najemcy", + "edit-customer-rest-limits-title":"Edytuj limity szybkości żądań REST dla klienta", + "edit-ws-limit-updates-per-session-title":"Edytuj limity szybkości aktualizacji WS na sesję", + "edit-cassandra-tenant-limits-configuration-title":"Edytuj limity szybkości zapytań do Cassandry dla najemcy", + "edit-tenant-entity-export-rate-limit-title":"Edytuj limity szybkości tworzenia wersji jednostki dla najemcy", + "edit-tenant-entity-import-rate-limit-title":"Edytuj limity szybkości wczytywania wersji jednostki dla najemcy", + "edit-tenant-notification-request-rate-limit-title":"Edytuj limity szybkości żądań powiadomień dla najemcy", + "edit-tenant-notification-requests-per-rule-rate-limit-title":"Edytuj limity szybkości żądań powiadomień na regułę powiadomień dla najemcy", + "messages-per":"komunikatów na", + "not-set":"Nie ustawiono", + "number-of-messages":"Liczba komunikatów", + "number-of-messages-required":"Wymagana liczba komunikatów.", + "number-of-messages-min":"Minimalna wartość to 1.", + "preview":"Podgląd", + "per-seconds":"Na sekundę", + "per-seconds-required":"Wymagana jest szybkość czasowa.", + "per-seconds-min":"Minimalna wartość to 1.", + "rate-limits":"Limity szybkości", + "remove-limit":"Usuń limit", + "transport-tenant-msg":"Przesyłanie komunikatów najemcy", + "transport-tenant-telemetry-msg":"Przesyłanie telemetrii komunikatów najemcy", + "transport-tenant-telemetry-data-points":"Przesyłanie punktów danych telemetrii najemcy", + "transport-device-msg":"Przesyłanie komunikatów urządzenia", + "transport-device-telemetry-msg":"Przesyłanie telemetrii komunikatów urządzenia", + "transport-device-telemetry-data-points":"Przesyłanie punktów danych telemetrii urządzenia", + "sec":"sek" + } + }, + "timeinterval":{ + "seconds-interval":"{ seconds, plural, =1 {1 sekunda} other {# sekundy} }", + "minutes-interval":"{ minutes, plural, =1 {1 minuta} other {# minuty} }", + "hours-interval":"{ hours, plural, =1 {1 godzina} other {# godziny} }", + "days-interval":"{ days, plural, =1 {1 dzień} other {# dni} }", + "days":"Dni", + "hours":"Godziny", + "minutes":"Minuty", + "seconds":"Sekundy", + "advanced":"Zaawansowane", + "predefined":{ + "yesterday":"Wczoraj", + "day-before-yesterday":"Przedwczoraj", + "this-day-last-week":"Ten dzień w zeszłym tygodniu", + "previous-week":"Poprzedni tydzień (Ndz - Sob)", + "previous-week-iso":"Poprzedni tydzień (Pon - Ndz)", + "previous-month":"Poprzedni miesiąc", + "previous-quarter":"Poprzedni kwartał", + "previous-half-year":"Poprzednie półrocze", + "previous-year":"Poprzedni rok", + "current-hour":"Bieżąca godzina", + "current-day":"Bieżący dzień", + "current-day-so-far":"Bieżący dzień do tej pory", + "current-week":"Bieżący tydzień (Ndz - Sob)", + "current-week-iso":"Bieżący tydzień (Pon - Ndz)", + "current-week-so-far":"Bieżący tydzień do tej pory (Ndz - Sob)", + "current-week-iso-so-far":"Bieżący tydzień do tej pory (Pon - Ndz)", + "current-month":"Bieżący miesiąc", + "current-month-so-far":"Bieżący miesiąc do tej pory", + "current-quarter":"Bieżący kwartał", + "current-quarter-so-far":"Bieżący kwartał do tej pory", + "current-half-year":"Bieżące półrocze", + "current-half-year-so-far":"Bieżące półrocze do tej pory", + "current-year":"Bieżący rok", + "current-year-so-far":"Bieżący rok do tej pory" + }, + "type":{ + "week":"Tydzień (Ndz - Sob)", + "week-iso":"Tydzień (Pon - Ndz)", + "month":"Miesiąc", + "quarter":"Kwartał" + } + }, + "timeunit":{ + "milliseconds":"Milisekundy", + "seconds":"Sekundy", + "minutes":"Minuty", + "hours":"Godziny", + "days":"Dni" + }, + "timewindow":{ + "timewindow":"Okno czasowe", + "years":"{ years, plural, =1 { rok } other {# lata } }", + "years-short":"{{ years }}r", + "months":"{ months, plural, =1 { miesiąc } other {# miesiące } }", + "months-short":"{{ months }}m", + "weeks":"{ weeks, plural, =1 { tydzień } other {# tygodnie } }", + "weeks-short":"{{ weeks }}t", + "days":"{ days, plural, =1 { dzień } other {# dni } }", + "days-short":"{{ days }}d", + "hours":"{ hours, plural, =0 { godzina } =1 {1 godzina } other {# godziny } }", + "hr":"{{ hr }} godz", + "hr-short":"{{ hr }}h", + "minutes":"{ minutes, plural, =0 { minuta } =1 {1 minuta } other {# minuty } }", + "min":"{{ min }} min", + "min-short":"{{ min }}m", + "seconds":"{ seconds, plural, =0 { sekunda } =1 {1 sekunda } other {# sekundy } }", + "sec":"{{ sec }} sek", + "sec-short":"{{ sec }}s", + "short":{ + "days":"{ days, plural, =1 {1 dzień } other {# dni } }", + "hours":"{ hours, plural, =1 {1 godzina } other {# godziny } }", + "minutes":"{{minutes}} min ", + "seconds":"{{seconds}} sek " + }, + "realtime":"Na żywo", + "history":"Historia", + "last-prefix":"ostatni", + "period":"od {{ startTime }} do {{ endTime }}", + "edit":"Edytuj okno czasowe", + "date-range":"Zakres dat", + "for-all-time":"Dla wszystkich dostępnych danych", + "last":"Ostatnie", + "time-period":"Okres czasu", + "hide":"Ukryj", + "interval":"Interwał", + "just-now":"Teraz", + "just-now-lower":"teraz", + "ago":"temu", + "style":"Styl okna czasowego", + "icon":"Ikona", + "icon-position":"Pozycja ikony", + "icon-position-left":"Lewo", + "icon-position-right":"Prawo", + "font":"Czcionka", + "color":"Kolor", + "displayTypePrefix":"Wyświetlaj przedrostek Na żywo/Historia", + "preview":"Podgląd" + }, + "tooltip":{ + "value":"Wartość", + "date":"Data", + "background-color":"Kolor tła", + "background-blur":"Rozmycie tła" + }, + "unit":{ + "millimeter":"Milimetr", + "centimeter":"Centymetr", + "angstrom":"Angstrom", + "nanometer":"Nanometr", + "micrometer":"Mikrometr", + "meter":"Metr", + "kilometer":"Kilometr", + "inch":"Cal", + "foot":"Stopa", + "yard":"Jard", + "mile":"Mila", + "nautical-mile":"Mila morska", + "astronomical-unit":"Jednostka astronomiczna", + "reciprocal-metre":"Odwrócony metr", + "meter-per-meter":"Metr na metr", + "steradian":"Steradian", + "thou":"Thou", + "barleycorn":"Jęczmień", + "hand":"Dłoń", + "chain":"Ciąg", + "furlong":"Furlong", + "league":"Liga", + "fathom":"Fathom", + "cable":"Kabel", + "link":"Link", + "rod":"Ród", + "nanogram":"Nanogram", + "microgram":"Mikrogram", + "milligram":"Miligram", + "gram":"Gram", + "kilogram":"Kilogram", + "tonne":"Tonna", + "ounce":"Uncja", + "pound":"Funt", + "stone":"Stoun", + "hundredweight-count":"Cental", + "short-tons":"Krótka tona", + "dalton":"Dalton", + "grain":"Gran", + "drachm":"Drachma", + "quarter":"Quarter", + "slug":"Slug", + "carat":"Karad", + "cubic-millimeter":"Milimetr sześcienny", + "cubic-centimeter":"Centymetr sześcienny", + "cubic-meter":"Metr sześcienny", + "cubic-kilometer":"Kilometr sześcienny", + "microliter":"Mikrolitr", + "milliliter":"Mililitr", + "liter":"Litr", + "hectoliter":"Hektolitr", + "cubic-inch":"Cal sześcienny", + "cubic-foot":"Stopa sześcienna", + "cubic-yard":"Jard sześcienny", + "fluid-ounce":"Uncja płynu", + "pint":"Pinta", + "quart":"Kwarta", + "gallon":"Galona", + "oil-barrels":"Baryłki naftowe", + "cubic-meter-per-kilogram":"Metr sześcienny na kilogram", + "gill":"Gill", + "hogshead":"Hogshead", + "teaspoon":"Łyżeczka", + "tablespoon":"Łyżka stołowa", + "cup":"Kubek", + "celsius":"Stopnie Celsiusza", + "kelvin":"Kelwin", + "rankine":"Rankine", + "fahrenheit":"Stopnie Fahrenheita", + "percent":"Procent", + "meter-per-second":"Metr na sekundę", + "kilometer-per-hour":"Kilometr na godzinę", + "foot-per-second":"Stopa na sekundę", + "mile-per-hour":"Mila na godzinę", + "knot":"Węzeł", + "millimeters-per-minute":"Milimetrów na minutę", + "kilometer-per-hour-squared":"Kilometr na godzinę do kwadratu", + "foot-per-second-squared":"Stopa na sekundę do kwadratu", + "pascal":"Pascal", + "kilopascal":"Kilopascal", + "megapascal":"Megapascal", + "gigapascal":"Gigapascal", + "millibar":"Milibar", + "bar":"Bar", + "kilobar":"Kilobar", + "newton":"Newton", + "newton-meter":"Newton-metr", + "foot-pounds":"Stopy funtów", + "inch-pounds":"Calowe funty", + "newton-per-meter":"Newton na metr", + "atmospheres":"Atmosfery", + "pounds-per-square-inch":"Funtów na cal kwadratowy", + "torr":"Torr", + "inches-of-mercury":"Cale rtęci", + "pascal-per-square-meter":"Pascal na metr kwadratowy", + "pound-per-square-inch":"Funt na cal kwadratowy", + "newton-per-square-meter":"Newton na metr kwadratowy", + "kilogram-force-per-square-meter":"Kilogram-siła na metr kwadratowy", + "pascal-per-square-centimeter":"Pascal na centymetr kwadratowy", + "ton-force-per-square-inch":"Ton-siła na cal kwadratowy", + "kilonewton-per-square-meter":"Kilonewton na metr kwadratowy", + "newton-per-square-millimeter":"Newton na milimetr kwadratowy", + "microjoule":"Mikrodżul", + "millijoule":"Milidżul", + "joule":"Dżul", + "kilojoule":"Kilodżul", + "megajoule":"Megadżul", + "gigajoule":"Gigadżul", + "watt-hour":"Watogodzina", + "kilowatt-hour":"Kilowatogodzina", + "electron-volts":"Elektronowolt", + "joules-per-coulomb":"Dżule na kulomb", + "british-thermal-unit":"Brytyjska jednostka ciepła", + "foot-pound":"Stopa-funt", + "calorie":"Kaloria", + "small-calorie":"Mała kaloria", + "kilocalorie":"Kilokaloria", + "joule-per-kelvin":"Dżul na kelwin", + "joule-per-kilogram-kelvin":"Dżul na kilogram-kelwin", + "joule-per-kilogram":"Dżul na kilogram", + "watt-per-meter-kelvin":"Wat na metr-kelwin", + "joule-per-cubic-meter":"Dżul na metr sześcienny", + "therm":"Term", + "electric-dipole-moment":"Moment dipolowy elektryczny", + "magnetic-dipole-moment":"Moment dipolowy magnetyczny", + "debye":"Debye", + "coulomb-per-square-meter-per-volt":"Kulomb na metr kwadratowy na wolt", + "milliwatt":"Miliwat", + "microwatt":"Mikrowat", + "watt":"Wat", + "kilowatt":"Kilowat", + "megawatt":"Megawat", + "gigawatt":"Gigawat", + "metric-horsepower":"Koń mechaniczny metryczny", + "milliwatt-per-square-centimeter":"Miliwaty na centymetr kwadratowy", + "watt-per-square-centimeter":"Waty na centymetr kwadratowy", + "kilowatt-per-square-centimeter":"Kilowat na centymetr kwadratowy", + "milliwatt-per-square-meter":"Miliwaty na metr kwadratowy", + "watt-per-square-meter":"Waty na metr kwadratowy", + "kilowatt-per-square-meter":"Kilowaty na metr kwadratowy", + "watt-per-square-inch":"Waty na cal kwadratowy", + "kilowatt-per-square-inch":"Kilowaty na cal kwadratowy", + "horsepower":"Koń mechaniczny", + "btu-per-hour":"Brytyjskie jednostki ciepła na godzinę", + "coulomb":"Kulomb", + "millicoulomb":"Milikulomb", + "microcoulomb":"Mikrokulomb", + "picocoulomb":"Pikokulomb", + "coulomb-per-meter":"Kulomb na metr", + "coulomb-per-cubic-meter":"Kulomb na metr sześcienny", + "coulomb-per-square-meter":"Kulomb na metr kwadratowy", + "square-millimeter":"Metr kwadratowy", + "square-centimeter":"Centymetr kwadratowy", + "square-meter":"Metr kwadratowy", + "hectare":"Hektar", + "square-kilometer":"Kilometr kwadratowy", + "square-inch":"Cal kwadratowy", + "square-foot":"Stopa kwadratowa", + "square-yard":"Jard kwadratowy", + "acre":"Akr", + "square-mile":"Mila kwadratowa", + "are":"Ar", + "barn":"Barn", + "circular-inch":"Cal okrągły", + "milliampere-hour":"Miliamperogodzina", + "milliampere-hour-tags":"prąd elektryczny, przepływ prądu, ładunek elektryczny, pojemność prądu, przepływ elektryczności, przepływ elektryczny, godzina miliamperowa, miliamperogodziny, mAh", + "ampere-hours":"Amperogodziny", + "ampere-hours-tags":"prąd elektryczny, przepływ prądu, ładunek elektryczny, pojemność prądu, przepływ elektryczności, przepływ elektryczny, amper, amperogodziny, Ah", + "kiloampere-hours":"Kiloamperogodziny", + "kiloampere-hours-tags":"prąd elektryczny, przepływ prądu, ładunek elektryczny, pojemność prądu, przepływ elektryczności, przepływ elektryczny, kiloamperogodziny, kiloamperogodzina, kAh", + "nanoampere":"Nanoamper", + "nanoampere-tags":"prąd, ampery, nanoamper, nA", + "picoampere":"Pikoamper", + "picoampere-tags":"prąd, ampery, pikoamper, pA", + "microampere":"Mikroamper", + "microampere-tags":"prąd elektryczny, mikroamper, mikroampery, μA", + "milliampere":"Miliamper", + "milliampere-tags":"prąd elektryczny, miliamper, miliampery, mA", + "ampere":"Amper", + "ampere-tags":"prąd elektryczny, przepływ prądu, przepływ elektryczności, przepływ elektryczny, amper, ampery, amperaż, A", + "kiloamperes":"Kiloampery", + "kiloamperes-tags":"prąd elektryczny, przepływ prądu, kiloampery, kA", + "microampere-per-square-centimeter":"Mikroamper na centymetr kwadratowy", + "microampere-per-square-centimeter-tags":"Gęstość prądu, mikroamper na centymetr kwadratowy, µA/cm²", + "ampere-per-square-meter":"Amper na metr kwadratowy", + "ampere-per-square-meter-tags":"gęstość prądu, prąd na jednostkę powierzchni, amper na metr kwadratowy, A/m²", + "ampere-per-meter":"Amper na metr", + "ampere-per-meter-tags":"siła pola magnetycznego, intensywność pola magnetycznego, amper na metr, A/m", + "oersted":"Oersted", + "oersted-tags":"pole magnetyczne, oersted, Oe", + "bohr-magneton":"Magnetony Bohra", + "bohr-magneton-tags":"fizyka atomowa, moment magnetyczny, magneton Bohra, μB", + "ampere-meter-squared":"Amper-Metr Kwadratowy", + "ampere-meter-squared-tags":"moment magnetyczny, moment dipolowy, amper-metr kwadratowy, A·m²", + "ampere-meter":"Amper-Metr", + "ampere-meter-tags":"pole magnetyczne, pętla prądowa, amper-metr, A·m", + "nanovolt":"Nanowolt", + "picovolt":"Pikowolt", + "millivolts":"Milivolty", + "microvolts":"Mikrowolty", + "volt":"Wolt", + "kilovolts":"Kilowolty", + "dbmV":"dBmV", + "dbm":"dBm", + "volt-meter":"Woltomierz", + "kilovolt-meter":"Kilowoltomierz", + "megavolt-meter":"Megawoltomierz", + "microvolt-meter":"Mikrowoltomierz", + "millivolt-meter":"Milivoltomierz", + "nanovolt-meter":"Nanowoltomierz", + "ohm":"Om", + "microohm":"Mikroom", + "milliohm":"Miloom", + "kilohm":"Kiloom", + "megohm":"Megaom", + "gigohm":"Gigaom", + "hertz":"Herz", + "kilohertz":"Kilohertz", + "megahertz":"Megahertz", + "gigahertz":"Gigahertz", + "rpm":"Obroty na minutę", + "candela-per-square-meter":"Kandela na metr kwadratowy", + "candela":"Kandela", + "lumen":"Lumen", + "lux":"Luks", + "foot-candle":"Stopa-świeca", + "lumen-per-square-meter":"Lumen na metr kwadratowy", + "lux-second":"Luks-sekunda", + "lumen-second":"Lumen-sekunda", + "lumens-per-watt":"Lumeny na wat", + "absorbance":"Absorpcja", + "mole":"Mol", + "nanomole":"Nanomol", + "micromole":"Mikromol", + "millimole":"Milimol", + "kilomole":"Kilomol", + "mole-per-cubic-meter":"Mol na metr sześcienny", + "rssi":"RSSI", + "ppm":"Części na milion", + "ppb":"Części na miliard", + "micrograms-per-cubic-meter":"Mikrogramy na metr sześcienny", + "aqi":"AQI", + "gram-per-cubic-meter":"Gram na metr sześcienny", + "gram-per-kilogram":"Wilgotność właściwa", + "millimeters-per-second":"Milimetry na sekundę", + "neper":"Neper", + "bel":"Bel", + "decibel":"Decybel", + "meters-per-second-squared":"Metry na sekundę kwadratową", + "becquerel":"Bekerel", + "curie":"Kiri", + "gray":"Graj", + "sievert":"Sivert", + "roentgen":"Rentgen", + "cps":"Zliczenia na sekundę", + "rad":"Rad", + "rem":"Rem", + "dps":"Rozpady na sekundę", + "rutherford":"Rutherford", + "coulombs-per-kilogram":"Kulomby na kilogram", + "becquerels-per-cubic-meter":"Bekerel na metr sześcienny", + "curies-per-liter":"Kiri na litr", + "becquerels-per-second":"Bekerel na sekundę", + "curies-per-second":"Kiri na sekundę", + "gy-per-second":"Graj na sekundę", + "watt-per-steradian":"Wat na steradian", + "watt-per-square-metre-steradian":"Wat na metr kwadratowy-steradian", + "ph-level":"Poziom pH", + "turbidity":"Mętność", + "mg-per-liter":"Miligramy na litr", + "microsiemens-per-centimeter":"Mikrosiemeny na centymetr", + "millisiemens-per-meter":"Milisiemeny na metr", + "siemens-per-meter":"Siemeny na metr", + "kilogram-per-cubic-meter":"Kilogram na metr sześcienny", + "gram-per-cubic-centimeter":"Gram na centymetr sześcienny", + "kilogram-per-square-meter":"Kilogram na metr kwadratowy", + "milligram-per-milliliter":"Miligram na mililitr", + "milligram-per-cubic-meter":"Miligram na metr sześcienny", + "pound-per-cubic-foot":"Funt na stopę sześcienną", + "ounces-per-cubic-inch":"Uncje na cal sześcienny", + "tons-per-cubic-yard":"Tony na jard sześcienny", + "particle-density":"Gęstość cząstek", + "kilometers-per-liter":"Kilometry na litr", + "miles-per-gallon":"Mile na galon", + "liters-per-100-km":"Litrów na 100 kilometrów", + "gallons-per-mile":"Galony na milę", + "liters-per-hour":"Litrów na godzinę", + "gallons-per-hour":"Galony na godzinę", + "beats-per-minute":"Uderzenia na minutę", + "millimeters-of-mercury":"Milimetry słupa rtęci", + "milligrams-per-deciliter":"Miligramy na decylitr", + "g-force":"Siła G", + "kilonewton":"Kiloniuton", + "kilogram-force":"Kilogram-siła", + "pound-force":"Funt-siła", + "kilopound-force":"Kilofunt-siła", + "dyne":"Dyn", + "poundal":"Poundal", + "kip":"Kip", + "gal":"Gal", + "gravity":"Przyspieszenie grawitacyjne", + "hectopascal":"Hektopaskal", + "atmosphere":"Atmosfera", + "millibars":"Milibary", + "inch-of-mercury":"Cal słupa rtęci", + "richter-scale":"Skala Richtera", + "second":"Sekunda", + "minute":"Minuta", + "hour":"Godzina", + "day":"Dzień", + "week":"Tydzień", + "month":"Miesiąc", + "year":"Rok", + "cubic-foot-per-minute":"Stopy sześcienne na minutę", + "cubic-meters-per-hour":"Metry sześcienne na godzinę", + "cubic-meters-per-second":"Metry sześcienne na sekundę", + "liter-per-second":"Litr na sekundę", + "liter-per-minute":"Litr na minutę", + "gallons-per-minute":"Galony na minutę", + "cubic-foot-per-second":"Stopa sześcienna na sekundę", + "milliliters-per-minute":"Mililitrów na minutę", + "bit":"Bit", + "byte":"Bajt", + "kilobyte":"Kilobajt", + "megabyte":"Megabajt", + "gigabyte":"Gigabajt", + "terabyte":"Terabajt", + "petabyte":"Petabajt", + "exabyte":"Eksabajt", + "zettabyte":"Zettabajt", + "yottabyte":"Jottabajt", + "bit-per-second":"Bit na sekundę", + "kilobit-per-second":"Kilobit na sekundę", + "megabit-per-second":"Megabit na sekundę", + "gigabit-per-second":"Gigabit na sekundę", + "terabit-per-second":"Terabit na sekundę", + "byte-per-second":"Bajt na sekundę", + "kilobyte-per-second":"Kilobajt na sekundę", + "megabyte-per-second":"Megabajt na sekundę", + "gigabyte-per-second":"Gigabajt na sekundę", + "degree":"Stopień", + "radian":"Radian", + "gradian":"Grad", + "mil":"Mil", + "revolution":"Obrotów", + "siemens":"Siemens", + "millisiemens":"Milisiemens", + "microsiemens":"Mikrosiemens", + "kilosiemens":"Kilosiemens", + "megasiemens":"Megasiemens", + "gigasiemens":"Gigasiemens", + "farad":"Farad", + "millifarad":"Milifarad", + "microfarad":"Mikrofarad", + "nanofarad":"Nanofarad", + "picofarad":"Pikofarad", + "kilofarad":"Kilofarad", + "megafarad":"Megafarad", + "gigafarad":"Gigafarad", + "terfarad":"Terfarad", + "farad-per-meter":"Farad na metr", + "tesla":"Tesla", + "gauss":"Gaus", + "kilogauss":"Kilogaus", + "millitesla":"Millitesla", + "microtesla":"Mikrotesla", + "nanotesla":"Nanotesla", + "kilotesla":"Kilotesla", + "megatesla":"Megatesla", + "millitesla-square-meters":"Millitesla na metry kwadratowe", + "gamma":"Gamma", + "lambda":"Lambda", + "square-meter-per-second":"Metr kwadratowy na sekundę", + "square-centimeter-per-second":"Centymetr kwadratowy na sekundę", + "stoke":"Stoke", + "centistokes":"Centistoke", + "square-foot-per-second":"Stopa kwadratowa na sekundę", + "square-inch-per-second":"Cal kwadratowy na sekundę", + "pascal-second":"Paskal-sekunda", + "centipoise":"Centipoise", + "poise":"Poise", + "reynolds":"Reynolds", + "pound-per-foot-hour":"Funt na stopę kwadratową na godzinę", + "newton-second-per-square-meter":"Newton-sekunda na metr kwadratowy", + "dyne-second-per-square-centimeter":"Dyn-sekunda na centymetr kwadratowy", + "kilogram-per-meter-second":"Kilogram na metr-sekundę", + "tesla-square-meters":"Tesla na metry kwadratowe", + "maxwell":"Maxwell", + "tesla-per-meter":"Tesla na metr", + "gauss-per-centimeter":"Gaus na centymetr", + "weber":"Weber", + "microweber":"Mikroweber", + "milliweber":"Milliweber", + "gauss-square-centimeter":"Gaus na centymetr kwadratowy", + "kilogauss-square-centimeter":"Kilogaus na centymetr kwadratowy", + "henry":"Henry", + "millihenry":"Millihenry", + "microhenry":"Mikrohenry", + "nanohenry":"Nanohenry", + "henry-per-meter":"Henry na metr", + "tesla-meter-per-ampere":"Tesla na metr na amper", + "gauss-per-oersted":"Gaus na Oersted", + "kilogram-per-mole":"Kilogram na mol", + "gram-per-mole":"Gram na mol", + "milligram-per-mole":"Miligram na mol", + "joule-per-mole":"Dżul na mol", + "joule-per-mole-kelvin":"Dżul na mol-kelwin", + "millivolts-per-meter":"Milivolty na metr", + "volts-per-meter":"Volty na metr", + "kilovolts-per-meter":"Kilovolty na metr", + "radian-per-second":"Radian na sekundę", + "radian-per-second-squared":"Radian na sekundę kwadratową", + "revolutions-per-minute-per-second":"Przyspieszenie kątowe", + "revolutions-per-minute-per-second-squared":"Przyspieszenie kątowe", + "deg-per-second":"Stopnie na sekundę", + "degrees-brix":"Stopnie Brix", + "katal":"Katal", + "katal-per-cubic-metre":"Katal na metr sześcienny" + }, + "user":{ + "user":"Użytkownik", + "users":"Użytkownicy", + "customer-users":"Użytkownicy klienta", + "tenant-admins":"Administratorzy najemcy", + "sys-admin":"Administrator systemu", + "tenant-admin":"Administrator najemcy", + "customer":"Klient", + "anonymous":"Anonimowy", + "add":"Dodaj użytkownika", + "delete":"Usuń użytkownika", + "add-user-text":"Dodaj nowego użytkownika", + "no-users-text":"Nie znaleziono użytkowników", + "user-details":"Szczegóły użytkownika", + "delete-user-title":"Czy na pewno chcesz usunąć użytkownika '{{userEmail}}'?", + "delete-user-text":"Uważaj, po potwierdzeniu użytkownik i wszystkie powiązane dane staną się nieodwracalne.", + "delete-users-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 użytkownika} other {# użytkowników} }?", + "delete-users-action-title":"Usuń { count, plural, =1 {1 użytkownika} other {# użytkowników} }", + "delete-users-text":"Uważaj, po potwierdzeniu wszyscy wybrani użytkownicy zostaną usunięci, a wszystkie powiązane dane staną się nieodwracalne.", + "activation-email-sent-message":"E-mail aktywacyjny został wysłany pomyślnie!", + "resend-activation":"Wyślij ponownie aktywację", + "email":"Email", + "email-required":"Email jest wymagany.", + "invalid-email-format":"Nieprawidłowy format email.", + "first-name":"Imię", + "last-name":"Nazwisko", + "description":"Opis", + "default-dashboard":"Domyślny pulpit", + "always-fullscreen":"Zawsze na pełnym ekranie", + "select-user":"Wybierz użytkownika", + "no-users-matching":"Nie znaleziono użytkowników pasujących do '{{entity}}'.", + "user-required":"Użytkownik jest wymagany", + "activation-method":"Metoda aktywacji", + "display-activation-link":"Wyświetl link aktywacyjny", + "send-activation-mail":"Wyślij mail aktywacyjny", + "activation-link":"Link aktywacyjny użytkownika", + "activation-link-text":"Aby aktywować użytkownika, użyj następującego linku aktywacyjnego:", + "copy-activation-link":"Kopiuj link aktywacyjny", + "activation-link-copied-message":"Link aktywacyjny użytkownika został skopiowany do schowka", + "details":"Szczegóły", + "login-as-tenant-admin":"Zaloguj jako Administrator Najemcy", + "login-as-customer-user":"Zaloguj jako Użytkownik Klienta", + "search":"Szukaj użytkowników", + "selected-users":"{ count, plural, =1 {1 użytkownik} other {# użytkowników} } wybrano", + "disable-account":"Wyłącz konto użytkownika", + "enable-account":"Włącz konto użytkownika", + "enable-account-message":"Konto użytkownika zostało pomyślnie włączone!", + "disable-account-message":"Konto użytkownika zostało pomyślnie wyłączone!", + "copyId":"Kopiuj Id użytkownika", + "idCopiedMessage":"Id użytkownika zostało skopiowane do schowka", + "user-list":"Lista użytkowników", + "user-list-required":"Lista użytkowników jest wymagana" + }, + "value":{ + "type":"Typ wartości", + "string":"Ciąg znaków", + "string-value":"Wartość ciągu znaków", + "string-value-required":"Wartość ciągu znaków jest wymagana", + "integer":"Liczba całkowita", + "integer-value":"Wartość liczby całkowitej", + "integer-value-required":"Wartość liczby całkowitej jest wymagana", + "invalid-integer-value":"Nieprawidłowa wartość liczby całkowitej", + "double":"Podwójna", + "double-value":"Wartość podwójna", + "double-value-required":"Wartość podwójna jest wymagana", + "boolean":"Boolean", + "boolean-value":"Wartość Boolean", + "false":"Fałsz", + "true":"Prawda", + "long":"Długa", + "json":"JSON", + "json-value":"Wartość JSON", + "json-value-invalid":"Wartość JSON ma nieprawidłowy format", + "json-value-required":"Wartość JSON jest wymagana." + }, + "version-control":{ + "version-control":"Kontrola wersji", + "management":"Zarządzanie kontrolą wersji", + "search":"Szukaj wersji", + "branch":"Gałąź", + "default":"Domyślna", + "select-branch":"Wybierz gałąź", + "branch-required":"Gałąź jest wymagana", + "create-entity-version":"Utwórz wersję encji", + "version-name":"Nazwa wersji", + "version-name-required":"Nazwa wersji jest wymagana", + "author":"Autor", + "export-relations":"Eksportuj relacje", + "export-attributes":"Eksportuj atrybuty", + "export-credentials":"Eksportuj poświadczenia", + "entity-versions":"Wersje encji", + "versions":"Wersje", + "created-time":"Czas utworzenia", + "version-id":"ID wersji", + "no-entity-versions-text":"Nie znaleziono wersji encji", + "no-versions-text":"Nie znaleziono wersji", + "copy-full-version-id":"Kopiuj pełne ID wersji", + "create-version":"Utwórz wersję", + "creating-version":"Tworzenie wersji... Proszę czekać", + "nothing-to-commit":"Brak zmian do zatwierdzenia", + "restore-version":"Przywróć wersję", + "restore-entity-from-version":"Przywróć encję z wersji '{{versionName}}'", + "restoring-entity-version":"Przywracanie wersji encji... Proszę czekać", + "load-relations":"Załaduj relacje", + "load-attributes":"Załaduj atrybuty", + "load-credentials":"Załaduj poświadczenia", + "compare-with-current":"Porównaj z obecną", + "diff-entity-with-version":"Różnice z wersją encji '{{versionName}}'", + "previous-difference":"Poprzednia różnica", + "next-difference":"Następna różnica", + "current":"Obecna", + "differences":"{ count, plural, =1 {1 różnica} other {# różnice} }", + "create-entities-version":"Utwórz wersję encji", + "default-sync-strategy":"Domyślna strategia synchronizacji", + "sync-strategy-merge":"Scal", + "sync-strategy-overwrite":"Nadpisz", + "entities-to-export":"Encje do eksportu", + "entities-to-restore":"Encje do przywrócenia", + "sync-strategy":"Strategia synchronizacji", + "all-entities":"Wszystkie encje", + "no-entities-to-export-prompt":"Proszę określić encje do eksportu", + "no-entities-to-restore-prompt":"Proszę określić encje do przywrócenia", + "add-entity-type":"Dodaj typ encji", + "remove-all":"Usuń wszystko", + "version-create-result":"{ added, plural, =0 {Nie dodano encji} =1 {Dodano 1 encję} other {Dodano # encji} }.
{ modified, plural, =0 {Nie zmodyfikowano encji} =1 {Zmodyfikowano 1 encję} other {Zmodyfikowano # encji} }.
{ removed, plural, =0 {Nie usunięto encji} =1 {Usunięto 1 encję} other {Usunięto # encji} }.", + "remove-other-entities":"Usuń inne encje", + "find-existing-entity-by-name":"Znajdź istniejącą encję po nazwie", + "restore-entities-from-version":"Przywróć encje z wersji '{{versionName}}'", + "restoring-entities-from-version":"Przywracanie encji... Proszę czekać", + "no-entities-restored":"Nie przywrócono żadnych encji", + "created":"{{created}} utworzonych", + "updated":"{{updated}} zaktualizowanych", + "deleted":"{{deleted}} usuniętych", + "remove-other-entities-confirm-text":"Uważaj! To trwale usunie wszystkie bieżące encje
nieobecne w wersji, którą chcesz przywrócić.

Proszę wpisać \"usuń inne encje\", aby potwierdzić.", + "auto-commit-to-branch":"automatyczne zatwierdzenie do gałęzi {{ branch }}", + "default-create-entity-version-name":"Aktualizacja {{entityName}}", + "sync-strategy-merge-hint":"Tworzy lub aktualizuje wybrane encje w repozytorium. Wszystkie inne encje w repozytorium są niezmodyfikowane.", + "sync-strategy-overwrite-hint":"Tworzy lub aktualizuje wybrane encje w repozytorium. Wszystkie inne encje w repozytorium są usunięte.", + "device-credentials-conflict":"Nie udało się załadować urządzenia z zewnętrznym id {{entityId}}
ponieważ te same poświadczenia są już obecne w bazie danych dla innego urządzenia.
Rozważ wyłączenie opcji załaduj poświadczenia w formularzu przywracania.", + "missing-referenced-entity":"Nie udało się załadować {{sourceEntityTypeName}} z zewnętrznym id {{sourceEntityId}}
ponieważ odnosi się do brakującej {{targetEntityTypeName}} z id {{targetEntityId}}.", + "runtime-failed":"Failed: {{message}}", + "auto-commit-settings-read-only-hint":"Funkcja automatycznego zatwierdzania nie działa z włączoną opcją tylko do odczytu w ustawieniach repozytorium." + }, + "widget":{ + "widget-library":"Biblioteka widgetów", + "widget-bundle":"Pakiet widgetów", + "all-bundles":"Wszystkie pakiety", + "select-widgets-bundle":"Wybierz pakiet widgetów", + "widgets":"Widgety", + "all-widgets":"Wszystkie widgety", + "widget":"Widget", + "select-widget":"Wybierz widget", + "no-widgets-matching":"Nie znaleziono widgetów pasujących do '{{entity}}'.", + "no-widgets":"Brak widgetów", + "no-widgets-text":"Nie znaleziono widgetów", + "management":"Zarządzanie widgetami", + "editor":"Edytor widgetów", + "confirm-to-exit-editor-html":"Masz niezapisane ustawienia widgetu.
Czy na pewno chcesz opuścić tę stronę?", + "widget-type-not-found":"Problem z ładowaniem konfiguracji widgetu.
Prawdopodobnie powiązany typ widgetu został usunięty.", + "widget-type-load-error":"Widget nie został załadowany z powodu następujących błędów:", + "remove":"Usuń widget", + "delete":"Usuń widget", + "edit":"Edytuj widget", + "remove-widget-title":"Czy na pewno chcesz usunąć widget '{{widgetTitle}}'?", + "remove-widget-text":"Po potwierdzeniu widget i wszystkie powiązane dane staną się nieodwracalne.", + "timeseries":"Serie czasowe", + "search-data":"Szukaj danych", + "no-data-found":"Nie znaleziono danych", + "latest":"Najnowsze wartości", + "rpc":"Widget sterowania", + "alarm":"Widget alarmów", + "static":"Widget statyczny", + "timeseries-short":"serie", + "latest-short":"najnowsze", + "rpc-short":"sterowanie", + "alarm-short":"alarm", + "static-short":"statyczny", + "select-widget-type":"Wybierz typ widgetu", + "missing-widget-title-error":"Tytuł widgetu musi być określony!", + "widget-saved":"Widget zapisany", + "unable-to-save-widget-error":"Nie można zapisać widgetu! Widget zawiera błędy!", + "save":"Zapisz widget", + "saveAs":"Zapisz widget jako", + "move":"Przenieś widget", + "save-widget-as":"Zapisz widget jako", + "save-widget-as-text":"Proszę wprowadzić nowy tytuł widgetu", + "toggle-fullscreen":"Przełącz pełny ekran", + "run":"Uruchom widget", + "widget-title":"Tytuł widgetu", + "title":"Tytuł", + "title-required":"Tytuł widgetu jest wymagany.", + "title-max-length":"Tytuł powinien mieć mniej niż 256 znaków", + "system":"System", + "type":"Typ widgetu", + "resources":"Zasoby", + "resource-url":"URL JavaScript/CSS", + "resource-is-module":"Jest modułem", + "remove-resource":"Usuń zasób", + "add-resource":"Dodaj zasób", + "html":"HTML", + "tidy":"Uporządkuj", + "css":"CSS", + "settings-schema":"Schemat ustawień", + "datakey-settings-schema":"Schemat ustawień klucza danych", + "latest-datakey-settings-schema":"Schemat ustawień najnowszego klucza danych", + "widget-settings":"Ustawienia widgetu", + "description":"Opis", + "tags":"Tagi", + "image-preview":"Podgląd obrazu", + "settings-form-selector":"Selektor formularza ustawień", + "data-key-settings-form-selector":"Selektor formularza ustawień klucza danych", + "latest-data-key-settings-form-selector":"Selektor formularza ustawień najnowszego klucza danych", + "all":"Wszystkie", + "actual":"Aktualne", + "deprecated":"Przestarzałe", + "has-basic-mode":"Ma tryb podstawowy", + "basic-mode-form-selector":"Selektor formularza trybu podstawowego", + "basic-mode":"Podstawowy", + "advanced-mode":"Zaawansowany", + "javascript":"Javascript", + "js":"JS", + "delete-widget-title":"Czy na pewno chcesz usunąć widget '{{widgetName}}'?", + "delete-widget-text":"Po potwierdzeniu widget i wszystkie powiązane dane staną się nieodwracalne.", + "delete-widgets-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 widget} other {# widgetów} }?", + "delete-widgets-text":"Uważaj, po potwierdzeniu wszystkie wybrane widgety zostaną usunięte, a wszystkie powiązane dane staną się nieodwracalne.", + "delete-widget":"Usuń widget", + "widget-template-load-failed-error":"Nie udało się załadować szablonu widgetu!", + "details":"Szczegóły", + "widget-details":"Szczegóły widgetu", + "add":"Dodaj widget", + "add-existing-widget":"Dodaj istniejący widget", + "add-new-widget":"Dodaj nowy widget", + "search-widgets":"Szukaj widgetów", + "selected-widgets":"{ count, plural, =1 {1 widget} other {# widgetów} } wybrano", + "undo":"Cofnij zmiany widgetu", + "export":"Eksportuj widget", + "export-widgets":"Eksportuj widgety", + "import":"Importuj widget", + "no-data":"Brak danych do wyświetlenia na widżecie", + "data-overflow":"Widget wyświetla {{count}} z {{total}} encji", + "alarm-data-overflow":"Widget wyświetla alarmy dla {{allowedEntities}} (maksymalna dozwolona) encji z {{totalEntities}} encji", + "search":"Szukaj widgetu", + "filter":"Typ filtru widgetu", + "loading-widgets":"Ładowanie widgetów...", + "widget-template-error":"Nieprawidłowy szablon HTML widgetu." + }, + "widget-action":{ + "header-button":"Przycisk nagłówka widgetu", + "open-dashboard-state":"Przejdź do nowego stanu pulpitów", + "update-dashboard-state":"Zaktualizuj bieżący stan pulpitów", + "open-dashboard":"Przejdź do innego pulpitu", + "custom":"Akcja niestandardowa", + "custom-pretty":"Akcja niestandardowa (z szablonem HTML)", + "custom-pretty-error-title":"Błąd niestandardowego okna dialogowego", + "custom-pretty-template-error":"Nieprawidłowy szablon niestandardowego okna dialogowego.", + "custom-pretty-controller-error":"Wystąpił błąd podczas oceny funkcji niestandardowego okna dialogowego.", + "mobile-action":"Akcja mobilna", + "target-dashboard-state":"Docelowy stan pulpitu", + "target-dashboard-state-required":"Docelowy stan pulpitu jest wymagany", + "set-entity-from-widget":"Ustaw encję z widgetu", + "target-dashboard":"Docelowy pulpit", + "open-right-layout":"Otwórz prawy układ pulpitu (widok mobilny)", + "state-display-type":"Opcja wyświetlania stanu pulpitu", + "open-normal":"Normalnie", + "open-in-separate-dialog":"Otwórz w osobnym oknie dialogowym", + "open-in-popover":"Otwórz w dymku", + "dialog-title":"Tytuł okna dialogowego", + "dialog-hide-dashboard-toolbar":"Ukryj pasek narzędzi pulpitu w oknie dialogowym", + "dialog-width":"Szerokość okna dialogowego w procentach względem szerokości viewportu", + "dialog-height":"Wysokość okna dialogowego w procentach względem wysokości viewportu", + "dialog-size-range-error":"Wartość procentowa rozmiaru okna dialogowego powinna być w zakresie od 1 do 100.", + "popover-preferred-placement":"Preferowane umiejscowienie dymka", + "popover-placement-top":"Góra", + "popover-placement-topLeft":"Góra lewo", + "popover-placement-topRight":"Góra prawo", + "popover-placement-right":"Prawo", + "popover-placement-rightTop":"Prawo góra", + "popover-placement-rightBottom":"Prawo dół", + "popover-placement-bottom":"Dół", + "popover-placement-bottomLeft":"Dół lewo", + "popover-placement-bottomRight":"Dół prawo", + "popover-placement-left":"Lewo", + "popover-placement-leftTop":"Lewo góra", + "popover-placement-leftBottom":"Lewo dół", + "popover-hide-on-click-outside":"Ukryj dymek po kliknięciu na zewnątrz", + "popover-hide-dashboard-toolbar":"Ukryj pasek narzędzi pulpitu w dymku", + "popover-width":"Szerokość dymka w jednostkach przeglądarki (np. 100px, 25vw)", + "popover-height":"Wysokość dymka w jednostkach przeglądarki (np. 100px, 25vh)", + "popover-style":"Styl dymka", + "open-new-browser-tab":"Otwórz w nowej karcie przeglądarki", + "mobile":{ + "action-type":"Typ akcji mobilnej", + "action-type-required":"Typ akcji mobilnej jest wymagany", + "take-picture-from-gallery":"Wybierz zdjęcie z galerii", + "take-photo":"Zrób zdjęcie", + "map-direction":"Otwórz wskazówki dojazdu na mapie", + "map-location":"Otwórz lokalizację na mapie", + "scan-qr-code":"Skanuj kod QR", + "make-phone-call":"Wykonaj połączenie telefoniczne", + "get-location":"Pobierz lokalizację telefonu", + "take-screenshot":"Zrób zrzut ekranu" + } + }, + "widgets-bundle":{ + "current":"Aktualny zestaw", + "widgets-bundles":"Zestawy widżetów", + "widgets-bundle-widgets":"Widżety zestawu widżetów", + "add":"Dodaj zestaw widżetów", + "delete":"Usuń zestaw widżetów", + "title":"Tytuł", + "title-required":"Tytuł jest wymagany.", + "title-max-length":"Tytuł powinien być krótszy niż 256", + "description":"Opis", + "image-preview":"Podgląd obrazu", + "order":"Kolejność", + "add-widgets-bundle-text":"Dodaj nowy zestaw widżetów", + "no-widgets-bundles-text":"Nie znaleziono zestawów widżetów", + "empty":"Zestaw widżetów jest pusty", + "details":"Szczegóły", + "widgets-bundle-details":"Szczegóły zestawu widżetów", + "delete-widgets-bundle-title":"Czy na pewno chcesz usunąć zestaw widżetów '{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text":"Uważaj, po potwierdzeniu zestaw widżetów oraz wszystkie powiązane dane staną się nieodwracalne.", + "delete-widgets-bundles-title":"Czy na pewno chcesz usunąć { count, plural, =1 {1 zestaw widżetów} other {# zestawy widżetów} }?", + "delete-widgets-bundles-action-title":"Usuń { count, plural, =1 {1 zestaw widżetów} other {# zestawy widżetów} }", + "delete-widgets-bundles-text":"Uważaj, po potwierdzeniu wszystkie wybrane zestawy widżetów zostaną usunięte, a wszystkie powiązane dane staną się nieodwracalne.", + "no-widgets-bundles-matching":"Nie znaleziono zestawów widżetów pasujących do '{{widgetsBundle}}'.", + "widgets-bundle-required":"Zestaw widżetów jest wymagany.", + "system":"System", + "import":"Importuj zestaw widżetów", + "export":"Eksportuj zestaw widżetów", + "export-widgets-bundle-widgets-prompt":"Dołącz widżety zestawu w eksportowanych danych (w przeciwnym razie zostaną wyeksportowane tylko odniesienia do pełnych kwalifikacji nazw widżetów)", + "export-failed-error":"Nie można wyeksportować zestawu widżetów: {{error}}", + "create-new-widgets-bundle":"Utwórz nowy zestaw widżetów", + "widgets-bundle-file":"Plik zestawu widżetów", + "invalid-widgets-bundle-file-error":"Nie można zaimportować zestawu widżetów: Nieprawidłowa struktura danych zestawu widżetów.", + "search":"Szukaj zestawów widżetów", + "selected-widgets-bundles":"{ count, plural, =1 {1 wybrany zestaw widżetów} other {# wybrane zestawy widżetów} }", + "open-widgets-bundle":"Otwórz zestaw widżetów", + "loading-widgets-bundles":"Ładowanie zestawów widżetów..." + }, + "widget-config":{ + "data":"Dane", + "settings":"Ustawienia", + "advanced":"Zaawansowane", + "appearance":"Wygląd", + "widget-card":"Karta widżetu", + "mobile":"Mobilny", + "title":"Tytuł", + "title-tooltip":"Podpowiedź tytułu", + "general-settings":"Ustawienia ogólne", + "display-title":"Wyświetl tytuł widżetu", + "card-title":"Tytuł karty", + "drop-shadow":"Cień", + "enable-fullscreen":"Włącz pełny ekran", + "background-color":"Kolor tła", + "text-color":"Kolor tekstu", + "border-radius":"Promień obramowania", + "padding":"Odstęp", + "margin":"Margines", + "widget-style":"Styl widżetu", + "widget-css":"CSS widżetu", + "title-style":"Styl tytułu", + "mobile-mode-settings":"Ustawienia trybu mobilnego", + "order":"Kolejność", + "height":"Wysokość", + "mobile-hide":"Ukryj widżet w trybie mobilnym", + "desktop-hide":"Ukryj widżet w trybie desktopowym", + "units":"Specjalny symbol obok wartości", + "units-by-default":"Domyślne jednostki", + "decimals":"Liczba miejsc po przecinku", + "decimals-by-default":"Domyślna liczba miejsc po przecinku", + "default-data-key-parameter-hint":"Ten parametr dotyczy wszystkich wartości widżetu, chyba że zostanie nadpisany przez konfigurację klucza danych", + "units-short":"Jednostki", + "decimals-short":"Miejsca po przecinku", + "decimals-suffix":"miejsca po przecinku", + "timewindow":"Okno czasowe", + "use-dashboard-timewindow":"Użyj okna czasowego dashboardu", + "use-widget-timewindow":"Użyj okna czasowego widżetu", + "display-timewindow":"Wyświetl okno czasowe", + "legend":"Legenda", + "display-legend":"Wyświetl legendę", + "datasources":"Źródła danych", + "datasource":"Źródło danych", + "maximum-datasources":"Maksymalnie { count, plural, =1 {1 źródło danych jest dozwolone.} other {# źródeł danych jest dozwolonych} }", + "timeseries-key-error":"Należy określić co najmniej jeden klucz danych szeregów czasowych", + "datasource-type":"Typ", + "datasource-parameters":"Parametry", + "remove-datasource":"Usuń źródło danych", + "add-datasource":"Dodaj źródło danych", + "target-device":"Docelowe urządzenie", + "alarm-source":"Źródło alarmu", + "actions":"Akcje", + "action":"Akcja", + "add-action":"Dodaj akcję", + "search-actions":"Szukaj akcji", + "no-actions-text":"Nie znaleziono akcji", + "action-source":"Źródło akcji", + "action-source-required":"Źródło akcji jest wymagane.", + "action-name":"Nazwa", + "action-name-required":"Nazwa akcji jest wymagana.", + "action-name-not-unique":"Inna akcja o tej samej nazwie już istnieje.
Nazwa akcji powin.", + "action-icon":"Ikona", + "show-hide-action-using-function":"Pokaż/ukryj akcję za pomocą funkcji", + "action-type":"Typ", + "action-type-required":"Typ akcji jest wymagany.", + "edit-action":"Edytuj akcję", + "delete-action":"Usuń akcję", + "delete-action-title":"Usuń akcję widżetu", + "delete-action-text":"Czy na pewno chcesz usunąć akcję widżetu o nazwie '{{actionName}}'?", + "title-icon":"Ikona tytułu", + "display-icon":"Wyświetl ikonę tytułu", + "card-icon":"Ikona karty", + "icon":"Ikona", + "icon-color":"Kolor ikony", + "icon-size":"Rozmiar ikony", + "advanced-settings":"Zaawansowane ustawienia", + "data-settings":"Ustawienia danych", + "limits":"Limity", + "no-data-display-message":"Alternatywny komunikat \"Brak danych do wyświetlenia\"", + "data-page-size":"Maksymalna liczba encji na źródło danych", + "settings-component-not-found":"Nie znaleziono komponentu formularza ustawień dla selektora '{{selector}}'", + "preview":"Podgląd", + "set":"Ustaw", + "set-message":"Ustaw komunikat", + "advanced-title-style":"Zaawansowany styl tytułu", + "card-style":"Styl karty", + "text":"Tekst", + "background":"Tło", + "advanced-widget-style":"Zaawansowany styl widżetu", + "card-buttons":"Przyciski karty", + "show-card-buttons":"Pokaż przyciski karty", + "card-border-radius":"Promień obramowania karty", + "card-appearance":"Wygląd karty", + "color":"Kolor", + "tooltip":"Podpowiedź", + "units-required":"Jednostka jest wymagana." + }, + "widget-type":{ + "import":"Importuj typ widżetu", + "export":"Eksportuj typ widżetu", + "export-failed-error":"Nie można wyeksportować widżetu: {{error}}", + "widget-file":"Plik widżetu", + "invalid-widget-file-error":"Nie można zaimportować widżetu: Nieprawidłowa struktura danych widżetu." + }, + "widgets":{ + "background":{ + "background":"Tło", + "background-settings":"Ustawienia tła", + "background-type-image":"Obraz", + "background-type-color":"Kolor", + "image-url":"URL obrazu", + "overlay":"Nakładka", + "enable-overlay":"Włącz nakładkę", + "blur":"Rozmycie", + "preview":"Podgląd" + }, + "bar-chart":{ + "bar-appearance":"Wygląd słupka", + "label-on-bar":"Etykieta na słupku", + "value-on-bar":"Wartość na słupku", + "bar-chart-card-style":"Styl karty wykresu słupkowego" + }, + "battery-level":{ + "layout":"Układ", + "layout-vertical-solid":"Pionowy. Stały", + "layout-horizontal-solid":"Poziomy. Stały", + "layout-vertical-divided":"Pionowy. Podzielony", + "layout-horizontal-divided":"Poziomy. Podzielony", + "icon":"Ikona", + "value":"Wartość", + "auto-scale":"Automatyczne skalowanie", + "battery-level-color":"Kolor poziomu baterii", + "battery-shape-color":"Kolor kształtu baterii", + "battery-level-card-style":"Styl karty poziomu baterii", + "sections-count":"Liczba sekcji" + }, + "signal-strength":{ + "value":"Wartość", + "last-update":"Ostatnia aktualizacja", + "no-signal":"Brak sygnału", + "layout":"Układ", + "layout-wifi":"Wi-Fi", + "layout-cellular-bar":"Słupkowy komórkowy", + "icon":"Ikona", + "date":"Data", + "active-bars-color":"Kolor aktywnych pasków sygnału", + "inactive-bars-color":"Kolor nieaktywnych pasków sygnału", + "signal-strength-card-style":"Styl karty siły sygnału" + }, + "chart":{ + "common-settings":"Wspólne ustawienia", + "enable-stacking-mode":"Włącz tryb układania", + "selection":"Wybór zakresu czasowego", + "enable-selection-mode":"Włącz tryb wyboru", + "line-shadow-size":"Rozmiar cienia linii", + "display-smooth-lines":"Wyświetlaj gładkie (krzywe) linie", + "default-bar-width":"Domyślna szerokość słupka dla danych niezagregowanych (milisekundy)", + "bar-alignment":"Wyrównanie słupka", + "bar-alignment-left":"Lewo", + "bar-alignment-right":"Prawo", + "bar-alignment-center":"Środek", + "default-font":"Domyślna czcionka", + "default-font-size":"Domyślny rozmiar czcionki", + "default-font-color":"Domyślny kolor czcionki", + "thresholds-line-width":"Domyślna szerokość linii dla wszystkich progów", + "tooltip-settings":"Ustawienia podpowiedzi", + "tooltip":"Podpowiedź", + "show-tooltip":"Pokaż podpowiedź", + "hover-individual-points":"Najedź na pojedyncze punkty", + "show-cumulative-values":"Pokaż wartości kumulatywne w trybie układania", + "hide-zero-false-values":"Ukryj zera/fałszywe wartości z podpowiedzi", + "tooltip-value-format-function":"Funkcja formatująca wartość podpowiedzi", + "grid-settings":"Ustawienia siatki", + "show-vertical-lines":"Pokaż linie pionowe", + "show-horizontal-lines":"Pokaż linie poziome", + "grid-outline-border-width":"Szerokość krawędzi/ramki siatki (px)", + "primary-color":"Kolor podstawowy", + "background-color":"Kolor tła", + "ticks-color":"Kolor znaczników", + "xaxis-settings":"Ustawienia osi X", + "axis-title":"Tytuł osi", + "xaxis-tick-labels-settings":"Ustawienia etykiet osi X", + "show-tick-labels":"Pokaż etykiety osi", + "yaxis-settings":"Ustawienia osi Y", + "min-scale-value":"Minimalna wartość na skali", + "max-scale-value":"Maksymalna wartość na skali", + "yaxis-tick-labels-settings":"Ustawienia etykiet osi Y", + "tick-step-size":"Rozmiar kroku między znacznikami", + "number-of-decimals":"Liczba miejsc po przecinku do wyświetlenia", + "ticks-formatter-function":"Funkcja formatująca znaczniki", + "comparison-settings":"Ustawienia porównywania", + "enable-comparison":"Włącz porównywanie", + "time-for-comparison":"Okres porównywania", + "time-for-comparison-previous-interval":"Poprzedni interwał (domyślnie)", + "time-for-comparison-days":"Dzień temu", + "time-for-comparison-weeks":"Tydzień temu", + "time-for-comparison-months":"Miesiąc temu", + "time-for-comparison-years":"Rok temu", + "time-for-comparison-custom-interval":"Niestandardowy interwał", + "custom-interval-value":"Wartość niestandardowego interwału (ms)", + "comparison-x-axis-settings":"Ustawienia osi X porównywania", + "axis-position":"Pozycja osi", + "axis-position-top":"Góra (domyślnie)", + "axis-position-bottom":"Dół", + "custom-legend-settings":"Niestandardowe ustawienia legendy", + "enable-custom-legend":"Włącz niestandardową legendę (pozwoli to na korzystanie z atrybutów/czasu rzeczywistego w etykietach kluczy)", + "key-name":"Nazwa klucza", + "key-name-required":"Nazwa klucza jest wymagana", + "key-type":"Typ klucza", + "key-type-attribute":"Atrybut", + "key-type-timeseries":"Czas rzeczywisty", + "label-keys-list":"Lista kluczy do użycia w etykietach", + "no-label-keys":"Brak skonfigurowanych kluczy", + "add-label-key":"Dodaj nowy klucz", + "line-width":"Szerokość linii", + "color":"Kolor", + "data-is-hidden-by-default":"Dane są domyślnie ukryte", + "disable-data-hiding":"Wyłącz ukrywanie danych", + "remove-from-legend":"Usuń klucz danych z legendy", + "exclude-from-stacking":"Wyłącz z układania (dostępne w trybie „Układanie”)", + "line-settings":"Ustawienia linii", + "show-line":"Pokaż linię", + "fill-line":"Wypełnij linię", + "fill-line-opacity":"Przezroczystość wypełnienia linii", + "points-settings":"Ustawienia punktów", + "show-points":"Pokaż punkty", + "points-line-width":"Szerokość linii punktów", + "points-radius":"Promień punktów", + "point-shape":"Kształt punktu", + "point-shape-circle":"Okrąg", + "point-shape-cross":"Krzyż", + "point-shape-diamond":"Diament", + "point-shape-square":"Kwadrat", + "point-shape-triangle":"Trójkąt", + "point-shape-custom":"Niestandardowa funkcja", + "point-shape-draw-function":"Funkcja rysowania kształtu punktu", + "show-separate-axis":"Pokaż oddzielną oś", + "axis-position-left":"Lewo", + "axis-position-right":"Prawo", + "thresholds":"Progi", + "no-thresholds":"Brak skonfigurowanych progów", + "add-threshold":"Dodaj próg", + "show-values-for-comparison":"Pokaż historyczne wartości do porównania", + "comparison-values-label":"Etykieta historycznych wartości", + "comparison-line-color":"Kolor linii porównawczej", + "threshold-settings":"Ustawienia progu", + "use-as-threshold":"Użyj wartości klucza jako progu", + "threshold-line-width":"Szerokość linii progu", + "threshold-color":"Kolor progu", + "common-pie-settings":"Wspólne ustawienia wykresu kołowego", + "radius":"Promień", + "inner-radius":"Wewnętrzny promień", + "tilt":"Nachylenie", + "common-pie-settings-range-error":"Wartość powinna być w zakresie od 0 do 1", + "stroke-settings":"Ustawienia obrysu", + "width-pixels":"Szerokość (piksele)", + "show-labels":"Pokaż etykiety", + "animation-settings":"Ustawienia animacji", + "animated-pie":"Włącz animację kołową (eksperymentalne)", + "border-settings":"Ustawienia obramowania", + "border-width":"Szerokość obramowania", + "border-color":"Kolor obramowania", + "legend-settings":"Ustawienia legendy", + "display-legend":"Wyświetl legendę", + "labels-font-color":"Kolor czcionki etykiet", + "series":"Seria", + "add-series":"Dodaj serię", + "series-settings":"Ustawienia serii", + "remove-series":"Usuń serię", + "no-series":"Brak skonfigurowanych serii", + "no-series-error":"Należy określić co najmniej jedną serię", + "chart-appearance":"Wygląd wykresu", + "vertical-grid-lines":"Linie siatki pionowej", + "horizontal-grid-lines":"Linie siatki poziomej", + "chart-background":"Tło wykresu", + "grid-lines-color":"Kolor linii siatki", + "border":"Obramowanie", + "axis":"Oś", + "vertical-axis":"Oś pionowa", + "ticks":"Znaczniki", + "horizontal-axis":"Oś pozioma" + }, + "color":{ + "color-settings":"Ustawienia koloru", + "color-type-constant":"Stały", + "color-type-range":"Zakres", + "color-type-function":"Funkcja", + "color":"Kolor", + "value-range":"Zakres wartości", + "from":"Od", + "to":"Do", + "color-function":"Funkcja koloru", + "copy-color-settings-from":"Skopiuj ustawienia koloru z" + }, + "dashboard-state":{ + "dashboard-state-settings":"Ustawienia stanu pulpitu", + "dashboard-state":"Identyfikator stanu pulpitu", + "autofill-state-layout":"Domyślne wypełnianie wysokości układu stanu automatycznie", + "default-margin":"Domyślny margines widżetów", + "default-background-color":"Domyślny kolor tła", + "sync-parent-state-params":"Synchronizuj parametry stanu z nadrzędnym pulpitem" + }, + "date-range-picker-settings":{ + "date-range-picker-settings":"Ustawienia wybieraka zakresu dat", + "hide-date-range-picker":"Ukryj wybierak zakresu dat", + "picker-one-panel":"Wybierak zakresu dat jednopanelowy", + "picker-auto-confirm":"Automatyczne potwierdzanie wybieraka zakresu dat", + "picker-show-template":"Pokazuj szablon wybieraka zakresu dat", + "first-day-of-week":"Pierwszy dzień tygodnia", + "interval-settings":"Ustawienia interwału", + "hide-interval":"Ukryj interwał", + "initial-interval":"Początkowy interwał", + "interval-hour":"Godzina", + "interval-day":"Dzień", + "interval-week":"Tydzień", + "interval-two-weeks":"2 tygodnie", + "interval-month":"Miesiąc", + "interval-three-months":"3 miesiące", + "interval-six-months":"6 miesięcy", + "step-settings":"Ustawienia kroku", + "hide-step-size":"Ukryj rozmiar kroku", + "initial-step-size":"Początkowy rozmiar kroku", + "hide-labels":"Ukryj etykiety", + "use-session-storage":"Użyj przechowywania sesji", + "localizationMap":{ + "Sun":"Niedz.", + "Mon":"Pon.", + "Tue":"Wt.", + "Wed":"Śr.", + "Thu":"Czw.", + "Fri":"Piąt.", + "Sat":"Sob.", + "Jan":"Sty", + "Feb":"Lut", + "Mar":"Mar", + "Apr":"Kwi", + "May":"Maj", + "Jun":"Cze", + "Jul":"Lip", + "Aug":"Sie", + "Sep":"Wrz", + "Oct":"Paź", + "Nov":"Lis", + "Dec":"Gru", + "January":"Styczeń", + "February":"Luty", + "March":"Marzec", + "April":"Kwiecień", + "June":"Czerwiec", + "July":"Lipiec", + "August":"Sierpień", + "September":"Wrzesień", + "October":"Październik", + "November":"Listopad", + "December":"Grudzień", + "Custom Date Range":"Niestandardowy zakres dat", + "Date Range Template":"Szablon zakresu dat", + "Today":"Dziś", + "Yesterday":"Wczoraj", + "This Week":"Bieżący tydzień", + "Last Week":"Ostatni tydzień", + "This Month":"Bieżący miesiąc", + "Last Month":"Ostatni miesiąc", + "Year":"Rok", + "This Year":"Bieżący rok", + "Last Year":"Ostatni rok", + "Date picker":"Wybierak dat", + "Hour":"Godzina", + "Day":"Dzień", + "Week":"Tydzień", + "2 weeks":"2 Tygodnie", + "Month":"Miesiąc", + "3 months":"3 Miesiące", + "6 months":"6 Miesięcy", + "Custom interval":"Niestandardowy interwał", + "Interval":"Interwał", + "Step size":"Rozmiar kroku", + "Ok":"Ok" + } + }, + "doughnut":{ + "total":"Suma", + "layout":"Układ", + "layout-default":"Domyślny", + "layout-with-total":"Z sumą", + "auto-scale":"Automatyczne skalowanie", + "clockwise-layout":"Układ zgodny z ruchem wskazówek zegara", + "sort-series":"Sortuj serie według etykiety", + "central-total-value":"Centralna wartość sumy", + "tooltip-value-type-absolute":"Bezwzględna", + "tooltip-value-type-percentage":"Procentowa", + "doughnut-card-style":"Styl karty pączka" + }, + "entities-hierarchy":{ + "hierarchy-data-settings":"Ustawienia danych hierarchii", + "relations-query-function":"Funkcja zapytania o relacje węzła", + "has-children-function":"Funkcja sprawdzająca, czy węzeł ma dzieci", + "node-state-settings":"Ustawienia stanu węzła", + "node-opened-function":"Domyślna funkcja otwierania węzła", + "node-disabled-function":"Funkcja dezaktywacji węzła", + "display-settings":"Ustawienia wyświetlania", + "node-icon-function":"Funkcja ikony węzła", + "node-text-function":"Funkcja tekstu węzła", + "sort-settings":"Ustawienia sortowania", + "nodes-sort-function":"Funkcja sortowania węzłów" + }, + "edge":{ + "display-default-title":"Wyświetl domyślny tytuł" + }, + "gateway":{ + "general-settings":"Ustawienia ogólne", + "widget-title":"Tytuł widżetu", + "default-archive-file-name":"Domyślna nazwa pliku archiwum", + "device-type-for-new-gateway":"Typ urządzenia dla nowej bramy", + "messages-settings":"Ustawienia komunikatów", + "save-config-success-message":"Komunikat tekstowy o pomyślnie zapisanej konfiguracji bramy", + "device-name-exists-message":"Komunikat tekstowy, gdy urządzenie o podanej nazwie już istnieje", + "gateway-title":"Formularz bramy", + "read-only":"Tylko do odczytu", + "events-title":"Tytuł formularza zdarzeń bramy", + "events-filter":"Filtr zdarzeń", + "event-key-contains":"Klucz zdarzenia zawiera...", + "show-connector":"Pokaż dla konektora", + "connector-state-param-key":"Klucz parametru stanu konektora", + "status":"Status", + "message":"Wiadomość", + "created-time":"Czas utworzenia" + }, + "gauge":{ + "default-color":"Domyślny kolor", + "radial-gauge-settings":"Ustawienia miernika radialnego", + "ticks-settings":"Ustawienia podziałek", + "min-value":"Wartość minimalna", + "max-value":"Wartość maksymalna", + "min-value-short":"min", + "max-value-short":"max", + "start-ticks-angle":"Kąt początkowy podziałek", + "ticks-angle":"Kąt podziałek", + "major-ticks":"Podziałki główne", + "major-ticks-count":"Liczba podziałek głównych", + "major-ticks-color":"Kolor podziałek głównych", + "minor-ticks":"Podziałki dodatkowe", + "minor-ticks-count":"Liczba podziałek dodatkowych", + "minor-ticks-color":"Kolor podziałek dodatkowych", + "tick-numbers-font":"Czcionka numerów podziałek", + "unit-title-settings":"Ustawienia jednostki", + "show-unit-title":"Pokaż nazwę jednostki", + "unit-title":"Nazwa jednostki", + "title-font":"Czcionka tytułu", + "units-settings":"Ustawienia jednostek", + "units-font":"Czcionka jednostek", + "value-box-settings":"Ustawienia pola wartości", + "show-value-box":"Pokaż pole wartości", + "value-box":"Pole wartości", + "value-int":"Liczba cyfr dla części całkowitej wartości", + "value-text":"Tekst wartości", + "value-text-shadow":"Cień tekstu wartości", + "value-font":"Czcionka tekstu wartości", + "rect-stroke-color-start":"Kolor obwódki prostokąta - początek gradientu", + "rect-stroke-color-end":"Kolor obwódki prostokąta - koniec gradientu", + "background-color":"Kolor tła", + "shadow-color":"Kolor cienia", + "value-box-rect-stroke-color":"Kolor obwódki prostokąta pola wartości", + "value-box-rect-stroke-color-end":"Kolor obwódki prostokąta pola wartości - koniec gradientu", + "value-box-background-color":"Kolor tła pola wartości", + "value-box-shadow-color":"Kolor cienia pola wartości", + "plate-settings":"Ustawienia tarczy", + "show-plate-border":"Pokaż krawędź tarczy", + "plate-color":"Kolor tarczy", + "needle-settings":"Ustawienia wskazówki", + "needle-circle-size":"Rozmiar koła wskazówki", + "needle-color":"Kolor wskazówki", + "needle-color-start":"Kolor wskazówki - początek gradientu", + "needle-color-end":"Kolor wskazówki - koniec gradientu", + "needle-color-shadow-up":"Kolor górnej części cienia wskazówki", + "needle-color-shadow-down":"Cień", + "highlights-settings":"Ustawienia punktów kulminacyjnych", + "highlights-width":"Szerokość punktów kulminacyjnych", + "highlights":"Punkty kulminacyjne", + "highlight-from":"Od", + "highlight-to":"Do", + "highlight-color":"Kolor", + "no-highlights":"Brak skonfigurowanych punktów kulminacyjnych", + "add-highlight":"Dodaj punkt kulminacyjny", + "animation-settings":"Ustawienia animacji", + "enable-animation":"Animacja", + "animation-duration-rule":"Czas trwania i reguła animacji", + "animation-duration":"Czas trwania animacji", + "animation-rule":"Reguła animacji", + "animation-linear":"Liniowa", + "animation-quad":"Kwadratowa", + "animation-quint":"Kwintowa", + "animation-cycle":"Cykliczna", + "animation-bounce":"Odbijająca się", + "animation-elastic":"Elastyczna", + "animation-dequad":"Odwrócona kwadratowa", + "animation-dequint":"Odwrócona kwintowa", + "animation-decycle":"Odwrócona cykliczna", + "animation-debounce":"Odwrócona odbijająca się", + "animation-delastic":"Odwrócona elastyczna", + "linear-gauge-settings":"Ustawienia miernika liniowego", + "bar-stroke":"Obwódka słupka", + "bar-stroke-width":"Szerokość obwódki słupka", + "bar-stroke-color":"Kolor obwódki słupka", + "bar-background-color":"Kolor tła słupka - początek gradientu", + "bar-background-color-end":"Kolor tła słupka - koniec gradientu", + "progress-bar-color":"Kolor paska postępu", + "progress-bar":"Pasek postępu", + "progress-bar-color-start":"Kolor paska postępu - początek gradientu", + "progress-bar-color-end":"Kolor paska postępu - koniec gradientu", + "major-ticks-names":"Nazwy podziałek głównych", + "show-stroke-ticks":"Pokaż obwódki podziałek", + "major-ticks-font":"Czcionka podziałek głównych", + "border-color":"Kolor obramowania", + "border-width":"Szerokość obramowania", + "needle-circle":"Koło wskazówki", + "needle-circle-color":"Kolor koła wskazówki", + "animation-target":"Cel animacji", + "animation-target-needle":"Wskazówka", + "animation-target-plate":"Tarcza", + "common-settings":"Wspólne ustawienia miernika", + "gauge-type":"Typ miernika", + "gauge-type-arc":"Łuk", + "gauge-type-donut":"Pączek", + "gauge-type-horizontal-bar":"Słupkowy poziomy", + "gauge-type-vertical-bar":"Słupkowy pionowy", + "donut-start-angle":"Kąt początkowy pączka", + "bar-settings":"Ustawienia słupka", + "relative-bar-width":"Względna szerokość słupka", + "neon-glow-brightness":"Jasność efektu świetlnego, (0-100), 0 - wyłączony efekt", + "stripes-thickness":"Grubość pasków, 0 - brak pasków", + "rounded-line-cap":"Użyj zaokrąglonego zakończenia linii", + "bar-color-settings":"Ustawienia koloru słupka", + "use-precise-level-color-values":"Użyj precyzyjnych poziomów kolorów", + "bar-colors":"Kolory słupków, od dolnego do górnego", + "color":"Kolor", + "no-bar-colors":"Brak skonfigurowanych kolorów słupków", + "add-bar-color":"Dodaj kolor słupka", + "from":"Od", + "to":"Do", + "fixed-level-colors":"Kolory słupków z użyciem wartości granicznych", + "gauge-title-settings":"Ustawienia tytułu miernika", + "show-gauge-title":"Pokaż tytuł miernika", + "gauge-title":"Tytuł miernika", + "gauge-title-font":"Czcionka tytułu miernika", + "unit-title-and-timestamp-settings":"Ustawienia jednostki i znacznika czasowego", + "show-timestamp":"Pokaż znacznik czasu wartości", + "timestamp-format":"Format znacznika czasu", + "label-font":"Czcionka etykiety pod wartością", + "value-settings":"Ustawienia wartości", + "show-value":"Pokaż tekst wartości", + "min-max-settings":"Ustawienia etykiet minimalnej/maksymalnej", + "show-min-max":"Pokaż wartości minimalną i maksymalną", + "min-max-font":"Czcionka etykiet minimalnej i maksymalnej", + "show-ticks":"Pokaż podziały", + "tick-width":"Szerokość podziałek", + "tick-color":"Kolor podziałek", + "tick-values":"Wartości podziałek", + "no-tick-values":"Brak skonfigurowanych wartości podziałek", + "add-tick-value":"Dodaj wartość podziałki", + "gauge-appearance":"Wygląd miernika", + "units-title":"Tytuł jednostek", + "value":"Wartość", + "ticks":"Podziały", + "arrow-and-scale-color":"Kolor strzałki i skali domyślnej", + "scale-settings":"Ustawienia skali", + "scale":"Skala", + "scale-color":"Kolory skali", + "compass-appearance":"Wygląd kompasu", + "labels":"Etykiety", + "label-style":"Styl etykiety" + }, + "gpio":{ + "pin":"Pin", + "label":"Label", + "row":"Row", + "column":"Column", + "color":"Color", + "panel-settings":"Panel settings", + "background-color":"Background color", + "gpio-switches":"GPIO switches", + "no-gpio-switches":"No GPIO switches configured", + "add-gpio-switch":"Add GPIO switch", + "gpio-status-request":"GPIO status request", + "method-name":"Method name", + "method-body":"Method body", + "gpio-status-change-request":"GPIO status change request", + "parse-gpio-status-function":"Parse gpio status function", + "gpio-leds":"GPIO leds", + "no-gpio-leds":"No GPIO leds configured", + "add-gpio-led":"Add GPIO led" + }, + "html-card":{ + "html":"HTML", + "css":"CSS" + }, + "input-widgets":{ + "attribute-not-allowed":"Parametr atrybutu nie może być używany w tym widżecie", + "blocked-location":"Geolokalizacja jest zablokowana w twojej przeglądarce", + "claim-device":"Zgłoś urządzenie", + "claim-failed":"Nie udało się zgłosić urządzenia!", + "claim-not-found":"Nie znaleziono urządzenia!", + "claim-successful":"Urządzenie zostało pomyślnie zgłoszone!", + "date":"Data", + "device-name":"Nazwa urządzenia", + "device-name-required":"Wymagana jest nazwa urządzenia", + "discard-changes":"Odrzuć zmiany", + "entity-attribute-required":"Wymagany jest atrybut encji", + "entity-coordinate-required":"Wymagane są obie pola, szerokość geograficzna i długość geograficzna", + "entity-timeseries-required":"Wymagany jest szereg czasowy encji", + "get-location":"Pobierz bieżącą lokalizację", + "invalid-date":"Nieprawidłowa data", + "latitude":"Szerokość geograficzna", + "longitude":"Długość geograficzna", + "min-value-error":"Wartość minimalna to {{value}}", + "max-value-error":"Wartość maksymalna to {{value}}", + "not-allowed-entity":"Wybrana encja nie może mieć współdzielonych atrybutów", + "no-attribute-selected":"Nie wybrano atrybutu", + "no-datakey-selected":"Nie wybrano klucza danych", + "no-coordinate-specified":"Nie określono klucza danych dla szerokości/długości geograficznej", + "no-entity-selected":"Nie wybrano encji", + "no-image":"Brak obrazu", + "no-support-geolocation":"Twoja przeglądarka nie obsługuje geolokalizacji", + "no-support-web-camera":"Twoja przeglądarka nie obsługuje kamer", + "enable-https-use-widget":"Proszę włączyć protokół HTTPS, aby korzystać z tego widżetu", + "no-found-your-camera":"Nie można znaleźć twojej kamery", + "no-permission-camera":"Użytkownik odmówił zgody / Ta witryna nie ma uprawnień do korzystania z kamery", + "no-timeseries-selected":"Nie wybrano szeregu czasowego", + "secret-key":"Tajny klucz", + "secret-key-required":"Wymagany jest tajny klucz", + "switch-attribute-value":"Przełącz wartość atrybutu encji", + "switch-camera":"Przełącz kamerę", + "switch-timeseries-value":"Przełącz wartość szeregu czasowego encji", + "take-photo":"Zrób zdjęcie", + "time":"Czas", + "timeseries-not-allowed":"Parametr szeregu czasowego nie może być używany w tym widżecie", + "update-failed":"Aktualizacja nieudana", + "update-successful":"Aktualizacja udana", + "update-attribute":"Zaktualizuj atrybut", + "update-timeseries":"Zaktualizuj szereg czasowy", + "value":"Wartość", + "general-settings":"Ustawienia ogólne", + "widget-title":"Tytuł widżetu", + "claim-button-label":"Etykieta przycisku zgłaszania", + "show-secret-key-field":"Pokaż pole wprowadzania „Tajny klucz”", + "labels-settings":"Ustawienia etykiet", + "show-labels":"Pokaż etykiety", + "device-name-label":"Etykieta pola wprowadzania nazwy urządzenia", + "secret-key-label":"Etykieta pola wprowadzania tajnego klucza", + "messages-settings":"Ustawienia wiadomości", + "claim-device-success-message":"Komunikat o udanym zgłoszeniu urządzenia", + "claim-device-not-found-message":"Komunikat, gdy urządzenie nie zostało znalezione", + "claim-device-failed-message":"Komunikat o nieudanym zgłoszeniu urządzenia", + "claim-device-name-required-message":"Komunikat błędu „Wymagana nazwa urządzenia”", + "claim-device-secret-key-required-message":"Komunikat błędu „Wymagany tajny klucz”", + "show-label":"Pokaż etykietę", + "label":"Etykieta", + "required":"Wymagane", + "required-error-message":"Komunikat błędu „Wymagane”", + "show-result-message":"Pokaż komunikat z wynikiem", + "integer-field-settings":"Ustawienia pola całkowitego", + "min-value":"Wartość minimalna", + "max-value":"Wartość maksymalna", + "double-field-settings":"Ustawienia pola liczbowego", + "text-field-settings":"Ustawienia pola tekstowego", + "min-length":"Minimalna długość", + "max-length":"Maksymalna długość", + "checkbox-settings":"Ustawienia pola wyboru", + "true-label":"Etykieta zaznaczenia", + "false-label":"Etykieta odznaczenia", + "image-input-settings":"Ustawienia wejścia obrazu", + "display-preview":"Wyświetl podgląd", + "display-clear-button":"Wyświetl przycisk Wyczyść", + "display-apply-button":"Wyświetl przycisk Zastosuj", + "display-discard-button":"Wyświetl przycisk Odrzuć", + "datetime-field-settings":"Ustawienia pola daty/czasu", + "display-time-input":"Wyświetl pole czasowe", + "latitude-key-name":"Nazwa klucza szerokości geograficznej", + "longitude-key-name":"Nazwa klucza długości geograficznej", + "show-get-location-button":"Pokaż przycisk „Pobierz bieżącą lokalizację”", + "use-high-accuracy":"Użyj wysokiej dokładności", + "location-fields-settings":"Ustawienia pól lokalizacji", + "latitude-label":"Etykieta dla szerokości geograficznej", + "longitude-label":"Etykieta dla długości geograficznej", + "input-fields-alignment":"Wyrównanie pól wprowadzania", + "input-fields-alignment-column":"Kolumna (domyślnie)", + "input-fields-alignment-row":"Rząd", + "layout":"Układ", + "row-gap":"Odstęp między rzędami w pikselach", + "column-gap":"Odstęp między kolumnami w pikselach", + "latitude-field-required":"Pole szerokości geograficznej jest wymagane", + "longitude-field-required":"Pole długości geograficznej jest wymagane", + "attribute-settings":"Ustawienia atrybutu", + "widget-mode":"Tryb widżetu", + "widget-mode-update-attribute":"Zaktualizuj atrybut", + "widget-mode-update-timeseries":"Zaktualizuj szereg czasowy", + "attribute-scope":"Zakres atrybutu", + "attribute-scope-server":"Atrybut serwera (domyślnie)", + "attribute-scope-shared":"Współdzielony atrybut", + "value-required":"Wartość wymagana", + "image-settings":"Ustawienia obrazu", + "image-format":"Format obrazu", + "image-format-jpeg":"JPEG", + "image-format-png":"PNG", + "image-format-webp":"WEBP", + "image-quality":"Jakość obrazu stosująca kompresję stratną, np. jpeg i webp", + "max-image-width":"Maksymalna szerokość obrazu", + "max-image-height":"Maksymalna wysokość obrazu", + "action-buttons":"Przyciski akcji", + "show-action-buttons":"Pokaż przyciski akcji", + "update-all-values":"Zaktualizuj wszystkie wartości, nie tylko zmodyfikowane", + "save-button-label":"Etykieta przycisku „ZAPISZ”", + "reset-button-label":"Etykieta przycisku „COFNIJ”", + "group-settings":"Ustawienia grupy", + "show-group-title":"Pokaż tytuł grupy pól związanych z różnymi encjami", + "group-title":"Tytuł grupy", + "fields-alignment":"Wyrównanie pól", + "fields-alignment-row":"Rząd (domyślnie)", + "fields-alignment-column":"Kolumna", + "fields-in-row":"Liczba pól w rzędzie", + "option-value":"Wartość (wpisz „null”, aby utworzyć pustą opcję)", + "option-label":"Etykieta", + "hide-input-field":"Ukryj pole wprowadzania", + "datakey-type":"Typ klucza danych", + "datakey-type-server":"Atrybut serwera (domyślnie)", + "datakey-type-shared":"Współdzielony atrybut", + "datakey-type-timeseries":"Szereg czasowy", + "datakey-value-type":"Typ wartości klucza danych", + "datakey-value-type-string":"Ciąg znaków", + "datakey-value-type-double":"Liczba zmiennoprzecinkowa", + "datakey-value-type-integer":"Liczba całkowita", + "datakey-value-type-json":"JSON", + "datakey-value-type-boolean-checkbox":"Boolean (Pole wyboru)", + "datakey-value-type-boolean-switch":"Boolean (Przełącznik)", + "datakey-value-type-date-time":"Data i godzina", + "datakey-value-type-date":"Data", + "datakey-value-type-time":"Czas", + "datakey-value-type-select":"Wybierz", + "datakey-value-type-color":"Kolor", + "value-is-required":"Wartość jest wymagana", + "ability-to-edit-attribute":"Zdolność do edycji atrybutu", + "ability-to-edit-attribute-editable":"Edytowalny (domyślnie)", + "ability-to-edit-attribute-disabled":"Wyłączony", + "ability-to-edit-attribute-readonly":"Tylko do odczytu", + "disable-on-datakey-name":"Wyłącz na fałszywą wartość innego klucza danych (podaj nazwę klucza danych)", + "field-appearance":"Wygląd pola", + "appearance-fill":"Wypełnienie", + "appearance-outline":"Obrys", + "subscript-sizing":"Rozmiar indeksu dolnego", + "subscript-sizing-fixed":"Stały", + "subscript-sizing-dynamic":"Dynamiczny", + "slide-toggle-settings":"Ustawienia przełącznika", + "slide-toggle-label-position":"Pozycja etykiety przełącznika", + "slide-toggle-label-position-after":"Po", + "slide-toggle-label-position-before":"Przed", + "select-options":"Opcje wyboru", + "no-select-options":"Brak skonfigurowanych opcji wyboru", + "add-select-option":"Dodaj opcję wyboru", + "numeric-field-settings":"Ustawienia pola numerycznego", + "step-interval":"Interwał kroku pomiędzy wartościami", + "error-messages":"Komunikaty o błędach", + "min-value-error-message":"Komunikat o błędzie 'Wartość minimalna'", + "max-value-error-message":"Komunikat o błędzie 'Wartość maksymalna'", + "invalid-date-error-message":"Komunikat o błędzie 'Nieprawidłowa data'", + "invalid-JSON-error-message":"Komunikat o błędzie 'Nieprawidłowy JSON'", + "icon-settings":"Ustawienia ikony", + "dialog-editor-settings":"Ustawienia edytora dialogu", + "use-custom-icon":"Użyj niestandardowej ikony", + "input-cell-icon":"Ikona do wyświetlenia przed komórką wejściową", + "value-conversion-settings":"Ustawienia konwersji wartości", + "get-value-settings":"Ustawienia pobierania wartości", + "use-get-value-function":"Użyj funkcji getValue", + "get-value-function":"Funkcja getValue", + "set-value-settings":"Ustawienia ustawiania wartości", + "use-set-value-function":"Użyj funkcji setValue", + "set-value-function":"Funkcja setValue", + "json-invalid":"Wartość JSON ma nieprawidłowy format", + "title":"Tytuł", + "cancel-button-label":"Etykieta przycisku 'Anuluj'" + }, + "invalid-qr-code-text":"Nieprawidłowy tekst wejściowy dla kodu QR. Wejście powinno być typu string.", + "qr-code":{ + "use-qr-code-text-function":"Użyj funkcji tekstowej kodu QR", + "qr-code-text-pattern":"Wzór tekstu kodu QR (np. '${entityName} | ${keyName} - jakiś tekst.')", + "qr-code-text-pattern-hint":"Wzór tekstu kodu QR używa wartości pierwszego znalezionego klucza w encjach w aliasie encji.", + "qr-code-text-pattern-required":"Wzór tekstu kodu QR jest wymagany.", + "qr-code-text-function":"Funkcja tekstu kodu QR" + }, + "label-widget":{ + "label-pattern":"Wzór", + "label-pattern-hint":"Wskazówka: na przykład 'Tekst ${keyName} jednostki.' lub ${#<index klucza>} jednostki'", + "label-pattern-required":"Wzór jest wymagany", + "label-position":"Położenie (Procentowo w stosunku do tła)", + "x-pos":"X", + "y-pos":"Y", + "background-color":"Kolor tła", + "font-settings":"Ustawienia czcionki", + "background-image":"Obraz tła", + "labels":"Etykiety", + "no-labels":"Brak skonfigurowanych etykiet", + "add-label":"Dodaj etykietę" + }, + "navigation":{ + "title":"Tytuł", + "navigation-path":"Ścieżka nawigacji", + "filter-type":"Typ filtru", + "filter-type-all":"Wszystkie elementy", + "filter-type-include":"Zawiera elementy", + "filter-type-exclude":"Wyklucza elementy", + "items":"Elementy", + "enter-urls-to-filter":"Wprowadź adresy URL do filtrowania..." + }, + "persistent-table":{ + "rpc-id":"ID RPC", + "message-type":"Typ wiadomości", + "method":"Metoda", + "params":"Parametry", + "created-time":"Czas utworzenia", + "expiration-time":"Czas wygaśnięcia", + "retries":"Próby", + "status":"Status", + "filter":"Filtr", + "refresh":"Odśwież", + "add":"Dodaj żądanie RPC", + "details":"Szczegóły", + "delete":"Usuń", + "delete-request-title":"Usuń trwałe żądanie RPC", + "delete-request-text":"Czy na pewno chcesz usunąć to żądanie?", + "details-title":"Szczegóły ID RPC: ", + "additional-info":"Dodatkowe informacje", + "response":"Odpowiedź", + "any-status":"Dowolny status", + "rpc-status-list":"Lista statusów RPC", + "no-request-prompt":"Brak żądań do wyświetlenia", + "send-request":"Wyślij żądanie", + "add-title":"Utwórz trwałe żądanie RPC", + "method-error":"Wymagana jest metoda.", + "timeout-error":"Minimalna wartość czasu oczekiwania to 5000 (5 sekund).", + "white-space-error":"Biała przestrzeń nie jest dozwolona.", + "rpc-status":{ + "QUEUED":"W KOLEJCE", + "SENT":"WYSŁANE", + "DELIVERED":"DOSTARCZONE", + "SUCCESSFUL":"UDANE", + "TIMEOUT":"PRZEKROCZONY LIMIT CZASU", + "EXPIRED":"WYGASŁE", + "FAILED":"NIEUDANE" + }, + "rpc-search-status-all":"WSZYSTKO", + "message-types":{ + "false":"Dwukierunkowe", + "true":"Jednokierunkowe" + }, + "general-settings":"Ustawienia ogólne", + "enable-filter":"Włącz filtr", + "enable-sticky-header":"Wyświetl nagłówek podczas przewijania", + "enable-sticky-action":"Wyświetl kolumnę akcji podczas przewijania", + "display-request-details":"Wyświetl szczegóły żądania", + "allow-send-request":"Zezwalaj na wysyłanie żądań RPC", + "allow-delete-request":"Zezwalaj na usuwanie żądań", + "columns-settings":"Ustawienia kolumn", + "display-columns":"Wyświetlane kolumny", + "column":"Kolumna", + "no-columns-found":"Nie znaleziono kolumn", + "no-columns-matching":"'{{column}}' nie znaleziono." + }, + "range-chart":{ + "chart":"Wykres", + "data-zoom":"Powiększenie danych", + "range-colors":"Kolory zakresu", + "out-of-range-color":"Kolor poza zakresem", + "fill-area":"Wypełnij obszar", + "range-chart-card-style":"Styl karty wykresu zakresu" + }, + "rpc":{ + "value-settings":"Ustawienia wartości", + "initial-value":"Początkowa wartość", + "retrieve-value-settings":"Ustawienia pobierania włączonej/wyłączonej wartości", + "retrieve-value-method":"Pobierz wartość za pomocą metody", + "retrieve-value-method-none":"Nie pobieraj", + "retrieve-value-method-rpc":"Wywołaj metodę pobierania wartości RPC", + "retrieve-value-method-attribute":"Subskrybuj atrybut", + "retrieve-value-method-timeseries":"Subskrybuj szereg czasowy", + "attribute-value-key":"Klucz atrybutu", + "timeseries-value-key":"Klucz szeregu czasowego", + "get-value-method":"Metoda RPC pobierz wartość", + "parse-value-function":"Funkcja analizy wartości", + "update-value-settings":"Ustawienia aktualizacji wartości", + "set-value-method":"Metoda RPC ustaw wartość", + "convert-value-function":"Funkcja konwersji wartości", + "rpc-settings":"Ustawienia RPC", + "request-timeout":"Limit czasu żądania RPC (ms)", + "persistent-rpc-settings":"Ustawienia trwałego RPC", + "request-persistent":"Trwałe żądanie RPC", + "persistent-polling-interval":"Interwał odpytywania (ms) w celu uzyskania odpowiedzi na trwałe polecenie RPC", + "common-settings":"Wspólne ustawienia", + "switch-title":"Tytuł przełącznika", + "show-on-off-labels":"Pokaż etykiety włącz/wyłącz", + "slide-toggle-label":"Etykieta przycisku przełącznika", + "label-position":"Pozycja etykiety", + "label-position-before":"Przed", + "label-position-after":"Po", + "slider-color":"Kolor suwaka", + "slider-color-primary":"Podstawowy", + "slider-color-accent":"Akcent", + "slider-color-warn":"Ostrzeżenie", + "button-style":"Styl przycisku", + "button-raised":"Podniesiony przycisk", + "button-primary":"Podstawowy kolor", + "button-background-color":"Kolor tła przycisku", + "button-text-color":"Kolor tekstu przycisku", + "widget-title":"Tytuł widżetu", + "button-label":"Etykieta przycisku", + "device-attribute-scope":"Zakres atrybutu urządzenia", + "server-attribute":"Atrybut serwera", + "shared-attribute":"Atrybut wspólny", + "device-attribute-parameters":"Parametry atrybutu urządzenia", + "is-one-way-command":"Jest to polecenie jednokierunkowe", + "rpc-method":"Metoda RPC", + "rpc-method-params":"Parametry metody RPC", + "show-rpc-error":"Pokaż błąd wykonania polecenia RPC", + "led-title":"Tytuł diody LED", + "led-color":"Kolor diody LED", + "check-status-settings":"Ustawienia sprawdzania statusu", + "perform-rpc-status-check":"Wykonaj sprawdzanie statusu urządzenia RPC", + "retrieve-led-status-value-method":"Pobierz stan diody LED za pomocą metody", + "led-status-value-attribute":"Atrybut urządzenia zawierający stan diody LED", + "led-status-value-timeseries":"Szereg czasowy urządzenia zawierający stan diody LED", + "check-status-method":"Metoda sprawdzania statusu urządzenia RPC", + "parse-led-status-value-function":"Funkcja analizy stanu diody LED", + "knob-title":"Tytuł pokrętła", + "min-value":"Minimalna wartość", + "max-value":"Maksymalna wartość" + }, + "maps":{ + "select-entity":"Wybierz jednostkę", + "select-entity-hint":"Wskazówka: po wybraniu kliknij na mapie, aby ustawić pozycję", + "tooltips":{ + "placeMarker":"Kliknij, aby umieścić jednostkę '{{entityName}}'", + "firstVertex":"Wielokąt dla '{{entityName}}': kliknij, aby umieścić pierwszy punkt", + "firstVertex-cut":"Kliknij, aby umieścić pierwszy punkt", + "continueLine":"Wielokąt dla '{{entityName}}': kliknij, aby kontynuować rysowanie", + "continueLine-cut":"Kliknij, aby kontynuować rysowanie", + "finishLine":"Kliknij na istniejący znacznik, aby zakończyć", + "finishPoly":"Wielokąt dla '{{entityName}}': kliknij na pierwszy znacznik, aby zakończyć i zapisać", + "finishPoly-cut":"Kliknij na pierwszy znacznik, aby zakończyć i zapisać", + "finishRect":"Wielokąt dla '{{entityName}}': kliknij, aby zakończyć i zapisać", + "startCircle":"Okrąg dla '{{entityName}}': kliknij, aby umieścić środek okręgu", + "finishCircle":"Okrąg dla '{{entityName}}': kliknij, aby zakończyć okrąg", + "placeCircleMarker":"Kliknij, aby umieścić znacznik okręgu" + }, + "actions":{ + "finish":"Zakończ", + "cancel":"Anuluj", + "removeLastVertex":"Usuń ostatni punkt" + }, + "buttonTitles":{ + "drawMarkerButton":"Umieść obiekt", + "drawPolyButton":"Utwórz wielokąt", + "drawLineButton":"Utwórz linię", + "drawCircleButton":"Utwórz okrąg", + "drawRectButton":"Utwórz prostokąt", + "editButton":"Tryb edycji", + "dragButton":"Tryb przeciągania", + "cutButton":"Odcinanie obszaru wielokąta", + "deleteButton":"Usuń", + "drawCircleMarkerButton":"Utwórz znacznik okręgu", + "rotateButton":"Obróć wielokąt" + }, + "map-provider-settings":"Ustawienia dostawcy map", + "map-provider":"Dostawca map", + "map-provider-google":"Google Maps", + "map-provider-openstreet":"OpenStreet Maps", + "map-provider-here":"HERE Maps", + "map-provider-image":"Mapa obrazu", + "map-provider-tencent":"Tencent Maps", + "openstreet-provider":"Dostawca mapy OpenStreet", + "openstreet-provider-mapnik":"OpenStreetMap.Mapnik (Domyślny)", + "openstreet-provider-hot":"OpenStreetMap.HOT", + "openstreet-provider-esri-street":"Esri.WorldStreetMap", + "openstreet-provider-esri-topo":"Esri.WorldTopoMap", + "openstreet-provider-esri-imagery":"Esri.WorldImagery", + "openstreet-provider-cartodb-positron":"CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter":"CartoDB.DarkMatter", + "use-custom-provider":"Użyj dostawcy niestandardowego", + "custom-provider-tile-url":"Niestandardowy adres URL kafelka dostawcy", + "google-maps-api-key":"Klucz API Google Maps", + "default-map-type":"Domyślny rodzaj mapy", + "google-map-type-roadmap":"Mapa drogowa", + "google-map-type-satelite":"Satelita", + "google-map-type-hybrid":"Hybrydowa", + "google-map-type-terrain":"Tereno", + "map-layer":"Warstwa mapy", + "here-map-normal-day":"HERE.normalDay (Domyślna)", + "here-map-normal-night":"HERE.normalNight", + "here-map-hybrid-day":"HERE.hybridDay", + "here-map-terrain-day":"HERE.terrainDay", + "credentials":"Poświadczenia", + "here-app-id":"ID aplikacji HERE", + "here-app-code":"Kod aplikacji HERE", + "here-api-key":"Klucz API HERE", + "here-use-new-version-api-3":"Użyj wersji API 3", + "tencent-maps-api-key":"Klucz API Tencent Maps", + "tencent-map-type-roadmap":"Mapa drogowa", + "tencent-map-type-satelite":"Satelita", + "tencent-map-type-hybrid":"Hybrydowa", + "image-map-background":"Tło mapy obrazu", + "image-map-background-from-entity-attribute":"Użyj tła mapy obrazu z atrybutu encji", + "image-url-source-entity-alias":"Źródło URL obrazu z aliasu encji", + "image-url-source-entity-attribute":"Źródło URL obrazu z atrybutu encji", + "common-map-settings":"Wspólne ustawienia mapy", + "x-pos-key-name":"Nazwa klucza X", + "y-pos-key-name":"Nazwa klucza Y", + "latitude-key-name":"Nazwa klucza szerokości geograficznej", + "longitude-key-name":"Nazwa klucza długości geograficznej", + "default-map-zoom-level":"Domyślny poziom przybliżenia mapy (0 - 20)", + "default-map-center-position":"Domyślna pozycja centralna mapy (0,0)", + "disable-scroll-zooming":"Wyłącz przybliżanie za pomocą przewijania", + "disable-double-click-zooming":"Wyłącz przybliżanie podwójnym kliknięciem", + "disable-zoom-control-buttons":"Wyłącz przyciski sterowania przybliżeniem", + "fit-map-bounds":"Dopasuj granice mapy, aby pokryć wszystkie markery", + "use-default-map-center-position":"Użyj domyślnej pozycji centralnej mapy", + "entities-limit":"Limit encji do wczytania", + "markers-settings":"Ustawienia znaczników", + "marker-offset-x":"Przesunięcie X znacznika względem pozycji pomnożone przez szerokość znacznika", + "marker-offset-y":"Przesunięcie Y znacznika względem pozycji pomnożone przez wysokość znacznika", + "position-function":"Funkcja konwersji pozycji, powinna zwracać współrzędne x, y jako liczby z zakresu od 0 do 1", + "draggable-marker":"Znacznik do przeciągania", + "label":"Etykieta", + "show-label":"Pokaż etykietę", + "use-label-function":"Użyj funkcji etykiety", + "label-pattern":"Etykieta (przykłady wzorców: '${entityName}', '${entityName}: (Tekst ${keyName} jednostki.)' )", + "label-function":"Funkcja etykiety", + "tooltip":"Podpowiedź", + "show-tooltip":"Pokaż podpowiedź", + "show-tooltip-action":"Działanie wyświetlania podpowiedzi", + "show-tooltip-action-click":"Pokaż podpowiedź po kliknięciu (Domyślnie)", + "show-tooltip-action-hover":"Pokaż podpowiedź po najechaniu", + "auto-close-tooltips":"Automatyczne zamykanie podpowiedzi", + "use-tooltip-function":"Użyj funkcji podpowiedzi", + "tooltip-pattern":"Podpowiedź (np. 'Tekst ${keyName} jednostki.' lub Tekst linku')", + "tooltip-function":"Funkcja podpowiedzi", + "tooltip-offset-x":"Przesunięcie X podpowiedzi względem kotwicy znacznika pomnożone przez szerokość znacznika", + "tooltip-offset-y":"Przesunięcie Y podpowiedzi względem kotwicy znacznika pomnożone przez wysokość znacznika", + "color":"Kolor", + "use-color-function":"Użyj funkcji koloru", + "color-function":"Funkcja koloru", + "marker-image":"Obraz znacznika", + "use-marker-image-function":"Użyj funkcji obrazu znacznika", + "custom-marker-image":"Niestandardowy obraz znacznika", + "custom-marker-image-size":"Niestandardowy rozmiar obrazu znacznika (px)", + "marker-image-function":"Funkcja obrazu znacznika", + "marker-images":"Obrazy znaczników", + "polygon-settings":"Ustawienia wielokąta", + "show-polygon":"Pokaż wielokąt", + "polygon-key-name":"Nazwa klucza wielokąta", + "enable-polygon-edit":"Włącz edycję wielokąta", + "polygon-label":"Etykieta wielokąta", + "show-polygon-label":"Pokaż etykietę wielokąta", + "use-polygon-label-function":"Użyj funkcji etykiety wielokąta", + "polygon-label-pattern":"Etykieta wielokąta (przykłady wzorców: '${entityName}', '${entityName}: (Tekst ${keyName} jednostki.)' )", + "polygon-label-function":"Funkcja etykiety wielokąta", + "polygon-tooltip":"Podpowiedź wielokąta", + "show-polygon-tooltip":"Pokaż podpowiedź wielokąta", + "auto-close-polygon-tooltips":"Automatyczne zamykanie podpowiedzi wielokąta", + "use-polygon-tooltip-function":"Użyj funkcji podpowiedzi wielokąta", + "polygon-tooltip-pattern":"Podpowiedź (np. 'Tekst ${keyName} jednostki.' lub Tekst linku')", + "polygon-tooltip-function":"Funkcja podpowiedzi wielokąta", + "polygon-color":"Kolor wielokąta", + "polygon-opacity":"Przezroczystość wielokąta", + "use-polygon-color-function":"Użyj funkcji koloru wielokąta", + "polygon-color-function":"Funkcja koloru wielokąta", + "polygon-stroke":"Obrys wielokąta", + "stroke-color":"Kolor obrysu", + "stroke-opacity":"Przezroczystość obrysu", + "stroke-weight":"Grubość obrysu", + "use-polygon-stroke-color-function":"Użyj funkcji koloru obrysu wielokąta", + "polygon-stroke-color-function":"Funkcja koloru obrysu wielokąta", + "circle-settings":"Ustawienia okręgu", + "show-circle":"Pokaż okrąg", + "circle-key-name":"Nazwa klucza okręgu", + "enable-circle-edit":"Włącz edycję okręgu", + "circle-label":"Etykieta okręgu", + "show-circle-label":"Pokaż etykietę okręgu", + "use-circle-label-function":"Użyj funkcji etykiety okręgu", + "circle-label-pattern":"Etykieta okręgu (przykłady wzorców: '${entityName}', '${entityName}: (Tekst ${keyName} jednostki.)' )", + "circle-label-function":"Funkcja etykiety okręgu", + "circle-tooltip":"Podpowiedź okręgu", + "show-circle-tooltip":"Pokaż podpowiedź okręgu", + "auto-close-circle-tooltips":"Automatyczne zamykanie podpowiedzi okręgu", + "use-circle-tooltip-function":"Użyj funkcji podpowiedzi okręgu", + "circle-tooltip-pattern":"Podpowiedź (np. 'Tekst ${keyName} jednostki.' lub Tekst linku')", + "circle-tooltip-function":"Funkcja podpowiedzi okręgu", + "circle-fill-color":"Kolor wypełnienia okręgu", + "circle-fill-color-opacity":"Przezroczystość koloru wypełnienia okręgu", + "use-circle-fill-color-function":"Użyj funkcji koloru wypełnienia okręgu", + "circle-fill-color-function":"Funkcja koloru wypełnienia okręgu", + "circle-stroke":"Obrys okręgu", + "use-circle-stroke-color-function":"Użyj funkcji koloru obrysu okręgu", + "circle-stroke-color-function":"Funkcja koloru obrysu okręgu", + "markers-clustering-settings":"Ustawienia grupowania znaczników", + "use-map-markers-clustering":"Użyj grupowania znaczników na mapie", + "zoom-on-cluster-click":"Powiększ po kliknięciu na klastrze", + "max-cluster-zoom":"Maksymalny poziom powiększenia, przy którym znacznik może być częścią klastra (0 - 18)", + "max-cluster-radius-pixels":"Maksymalny promień, który może obejmować klaster w pikselach", + "cluster-zoom-animation":"Pokaż animację znaczników podczas przybliżania", + "show-markers-bounds-on-cluster-mouse-over":"Pokaż granice znaczników po najechaniu myszą na klaster", + "spiderfy-max-zoom-level":"Rozpoznaj na najwyższym poziomie powiększenia (aby zobaczyć wszystkie znaczniki klastra)", + "load-optimization":"Optymalizacja ładowania", + "cluster-chunked-loading":"Użyj fragmentów do dodawania znaczników, aby strona się nie zawiesiła", + "cluster-markers-lazy-load":"Użyj ładowania opóźnionego do dodawania znaczników", + "editor-settings":"Ustawienia edytora", + "enable-snapping":"Włącz przyciąganie do innych wierzchołków dla precyzyjnego rysowania", + "init-draggable-mode":"Inicjalizuj mapę w trybie przeciągania", + "hide-all-edit-buttons":"Ukryj wszystkie przyciski edycji", + "hide-draw-buttons":"Ukryj przyciski rysowania", + "hide-edit-buttons":"Ukryj przyciski edycji", + "hide-remove-button":"Ukryj przycisk usuwania", + "route-map-settings":"Ustawienia mapy trasy", + "trip-animation-settings":"Ustawienia animacji podróży", + "normalization-step":"Krok normalizacji danych (ms)", + "tooltip-background-color":"Kolor tła podpowiedzi", + "tooltip-font-color":"Kolor czcionki podpowiedzi", + "tooltip-opacity":"Przezroczystość podpowiedzi (0-1)", + "auto-close-tooltip":"Automatyczne zamykanie podpowiedzi", + "rotation-angle":"Ustaw dodatkowy kąt obrotu dla znacznika (stopnie)", + "path-settings":"Ustawienia ścieżki", + "path-color":"Kolor ścieżki", + "use-path-color-function":"Użyj funkcji koloru ścieżki", + "path-color-function":"Funkcja koloru ścieżki", + "path-decorator":"Dekorator ścieżki", + "use-path-decorator":"Użyj dekoratora ścieżki", + "decorator-symbol":"Symbol dekoratora", + "decorator-symbol-arrow-head":"Strzała", + "decorator-symbol-dash":"Kreska", + "decorator-symbol-size":"Rozmiar symbolu dekoratora (px)", + "use-path-decorator-custom-color":"Użyj niestandardowego koloru dekoratora ścieżki", + "decorator-custom-color":"Niestandardowy kolor dekoratora", + "decorator-offset":"Przesunięcie dekoratora", + "end-decorator-offset":"Przesunięcie końcowe dekoratora", + "decorator-repeat":"Powtórzenie dekoratora", + "points-settings":"Ustawienia punktów", + "show-points":"Pokaż punkty", + "point-color":"Kolor punktu", + "point-size":"Rozmiar punktu (px)", + "use-point-color-function":"Użyj funkcji koloru punktu", + "point-color-function":"Funkcja koloru punktu", + "use-point-as-anchor":"Użyj punktu jako kotwicy", + "point-as-anchor-function":"Funkcja punktu jako kotwicy", + "independent-point-tooltip":"Niezależna podpowiedź punktu", + "clustering-markers":"Klastrowanie znaczników", + "use-icon-create-function":"Użyj funkcji koloru znaczników", + "marker-color-function":"Funkcja koloru znacznika" + }, + "markdown":{ + "use-markdown-text-function":"Użyj funkcji wartości markdown/HTML", + "markdown-text-function":"Funkcja wartości markdown/HTML", + "markdown-text-pattern":"Wzorzec markdown/HTML (markdown lub HTML z zmiennymi, np. '${entityName} lub ${keyName} - jakiś tekst.')", + "apply-default-markdown-style":"Zastosuj domyślny styl markdown", + "markdown-css":"CSS markdown/HTML" + }, + "simple-card":{ + "label":"Etykieta", + "label-position":"Pozycja etykiety", + "label-position-left":"Po lewej", + "label-position-top":"Na górze" + }, + "value-card":{ + "layout":"Układ", + "layout-square":"Kwadratowy", + "layout-vertical":"Pionowy", + "layout-centered":"Wyśrodkowany", + "layout-simplified":"Uproszczony", + "layout-horizontal":"Poziomy", + "layout-horizontal-reversed":"Odwrócony poziomy", + "label":"Etykieta", + "icon":"Ikona", + "value":"Wartość", + "date":"Data", + "value-card-style":"Styl karty wartości", + "auto-scale":"Automatyczne dostosowanie skali" + }, + "liquid-level-card":{ + "layout-simple":"Prosty", + "layout-percentage":"Procentowy", + "layout-absolute":"Bezwzględny", + "layout":"Układ", + "background-overlay":"Nakładka na tło wartości", + "total-volume":"Całkowita objętość", + "tank":"Zbiornik", + "shape":"Kształt", + "datasource-units":"Jednostki źródła danych", + "widget-units":"Jednostki widżetu", + "decimals":"Miejsca dziesiętne", + "liquid":"Ciecz", + "liquid-color":"Kolor cieczy", + "value":"Wartość", + "value-font":"Czcionka wartości", + "level":"Poziom", + "last-update":"Ostatnia aktualizacja", + "shape-by-attribute":"Ustaw kształt zbiornika według nazwy atrybutu", + "tooltip-background":"Kolor tła", + "background-blur":"Rozmycie tła", + "tank-color":"Kolor zbiornika", + "static":"Statyczny", + "see-examples":"Zobacz przykłady", + "attribute":"Atrybut", + "shape-type":"Typ", + "v-oval":"Pionowy Owal", + "v-cylinder":"Pionowy Cylinder", + "v-capsule":"Pionowa Kapsuła", + "rectangle":"Prostokąt", + "h-oval":"Poziomy Owal", + "h-ellipse":"Pozioma Elipsa", + "h-dish-ends":"Poziome Zakończenia Dzbanka", + "h-cylinder":"Poziomy Cylinder", + "h-capsule":"Pozioma Kapsuła", + "h-elliptical_2_1":"Pozioma Elipsa 2:1", + "icon":"Ikona karty", + "title":"Tytuł karty", + "units":"Jednostki", + "color-and-font":"Kolor i czcionka", + "shape-attribute-name":"Nazwa atrybutu", + "total-volume-required":"Wymagana jest całkowita objętość.", + "attribute-name-required":"Wymagana jest nazwa atrybutu.", + "attribute-key-not-set":"Klucz atrybutu '{{attributeName}}' nie jest ustawiony", + "attribute-key-invalid":"Klucz atrybutu '{{attributeName}}' jest nieprawidłowy" + }, + "aggregated-value-card":{ + "subtitle":"Podtytuł", + "chart":"Wykres", + "values":"Wartości", + "value-appearance":"Wygląd wartości", + "position":"Pozycja", + "position-center":"Środek", + "position-right-top":"Prawy górny róg", + "position-right-bottom":"Prawy dolny róg", + "position-left-top":"Lewy górny róg", + "position-left-bottom":"Lewy dolny róg", + "font":"Czcionka", + "color":"Kolor", + "arrow":"Strzałka", + "display-up-down-arrow":"Wyświetl strzałkę w górę/dół", + "add-value":"Dodaj wartość", + "remove-value":"Usuń wartość", + "no-values":"Brak skonfigurowanych wartości", + "aggregation":"Agregacja", + "aggregated-value-card-style":"Styl karty zagregowanej wartości", + "auto-scale":"Automatyczne skalowanie" + }, + "value-chart-card":{ + "layout":"Układ", + "layout-left":"Lewo", + "layout-right":"Prawo", + "auto-scale":"Automatyczna skala", + "icon":"Ikona", + "value":"Wartość", + "chart":"Wykres", + "value-chart-card-style":"Styl karty wartości z wykresem" + }, + "progress-bar":{ + "layout":"Układ", + "layout-default":"Domyślny", + "layout-simplified":"Uproszczony", + "auto-scale":"Automatyczna skala", + "icon":"Ikona", + "value":"Wartość", + "range":"Zakres", + "min":"min", + "max":"max", + "range-ticks":"Podziałka zakresu", + "bar":"Pasek", + "bar-color":"Kolor paska", + "bar-background":"Tło paska", + "progress-bar-card-style":"Styl karty paska postępu" + }, + "alarm-count":{ + "alarm-count-card-style":"Styl karty licznika alarmów" + }, + "entity-count":{ + "entity-count-card-style":"Entity count card style" + }, + "count":{ + "layout":"Układ", + "layout-column":"Kolumna", + "layout-row":"Wiersz", + "label":"Etykieta", + "icon":"Ikona", + "icon-background":"Tło ikony", + "value":"Wartość", + "chevron":"Strzałka", + "auto-scale":"Automatyczna skala" + }, + "table":{ + "common-table-settings":"Wspólne ustawienia tabeli", + "enable-search":"Włącz wyszukiwanie", + "enable-sticky-header":"Zawsze wyświetlaj nagłówek", + "enable-sticky-action":"Zawsze wyświetlaj kolumnę akcji", + "hidden-cell-button-display-mode":"Tryb wyświetlania ukrytego przycisku akcji w komórce", + "show-empty-space-hidden-action":"Pokaż pustą przestrzeń zamiast ukrytego przycisku akcji w komórce", + "dont-reserve-space-hidden-action":"Nie rezerwuj miejsca na ukryte przyciski akcji", + "display-timestamp":"Znak czasu", + "display-pagination":"Wyświetl paginację", + "default-page-size":"Domyślny rozmiar strony", + "use-entity-label-tab-name":"Użyj etykiety encji w nazwie karty", + "hide-empty-lines":"Ukryj puste wiersze", + "row-style":"Styl wiersza", + "use-row-style-function":"Użyj funkcji stylu wiersza", + "row-style-function":"Funkcja stylu wiersza", + "cell-style":"Styl komórki", + "use-cell-style-function":"Użyj funkcji stylu komórki", + "cell-style-function":"Funkcja stylu komórki", + "cell-content":"Zawartość komórki", + "use-cell-content-function":"Użyj funkcji zawartości komórki", + "cell-content-function":"Funkcja zawartości komórki", + "show-latest-data-column":"Pokaż kolumnę z najnowszymi danymi", + "latest-data-column-order":"Kolejność kolumny z najnowszymi danymi", + "entities-table-title":"Tytuł tabeli encji", + "enable-select-column-display":"Włącz wybór kolumn do wyświetlenia", + "display-entity-name":"Wyświetl kolumnę z nazwą encji", + "entity-name-column-title":"Tytuł kolumny z nazwą encji", + "display-entity-label":"Wyświetl kolumnę z etykietą encji", + "entity-label-column-title":"Tytuł kolumny z etykietą encji", + "display-entity-type":"Wyświetl kolumnę z typem encji", + "default-sort-order":"Domyślna kolejność sortowania", + "custom-title":"Niestandardowy tytuł nagłówka", + "column-width":"Szerokość kolumny (px lub %)", + "default-column-visibility":"Domyślna widoczność kolumny", + "column-visibility-visible":"Widoczna", + "column-visibility-hidden":"Ukryta", + "column-visibility-hidden-mobile":"Ukryta w trybie mobilnym", + "column-selection-to-display":"Wybór kolumn w 'Kolumny do wyświetlenia'", + "column-selection-to-display-enabled":"Włączony", + "column-selection-to-display-disabled":"Wyłączony", + "alarms-table-title":"Tytuł tabeli alarmów", + "enable-alarms-selection":"Włącz wybór alarmów", + "enable-alarms-search":"Włącz wyszukiwanie alarmów", + "enable-alarm-filter":"Włącz filtr alarmów", + "display-alarm-details":"Wyświetl szczegóły alarmu", + "allow-alarms-ack":"Zezwalaj na potwierdzanie alarmów", + "allow-alarms-clear":"Zezwalaj na usuwanie alarmów", + "display-alarm-activity":"Wyświetl aktywność alarmu", + "allow-alarms-assign":"Zezwalaj na przypisywanie alarmów", + "columns":"Kolumny", + "column-settings":"Ustawienia kolumn", + "remove-column":"Usuń kolumnę", + "add-column":"Dodaj kolumnę", + "no-columns":"Brak skonfigurowanych kolumn", + "columns-to-display":"Kolumny do wyświetlenia", + "table-header":"Nagłówek tabeli", + "header-buttons":"Przyciski nagłówka", + "table-buttons":"Przyciski tabeli", + "pagination":"Paginacja", + "rows":"Wiersze", + "timeseries-column-error":"Należy określić co najmniej jedną kolumnę szeregów czasowych", + "alarm-column-error":"Należy określić co najmniej jedną kolumnę alarmów", + "table-tabs":"Karty tabeli", + "show-cell-actions-menu-mobile":"Pokaż menu rozwijane akcji komórek w trybie mobilnym" + }, + "wind-speed-direction":{ + "layout":"Układ", + "layout-default":"Domyślny", + "layout-advanced":"Zaawansowany", + "layout-simplified":"Uproszczony", + "values":"Wartości", + "wind-direction":"Kierunek wiatru", + "center-value":"Wartość środkowa", + "icon":"Ikona", + "arrow":"Strzałka", + "ticks":"Kreski", + "labels-type":"Typ etykiet", + "directional-names":"Nazwy kierunkowe", + "degrees":"Stopnie", + "major-ticks":"Główne kreski", + "minor-ticks":"Drobne kreski", + "wind-speed-direction-card-style":"Styl karty prędkości i kierunku wiatru", + "ticks-color":"Kolor kresek", + "ticks-labels-type":"Typ etykiet kreskowych", + "arrow-color":"Kolor strzałki" + }, + "value-source":{ + "value-source":"Źródło wartości", + "predefined-value":"Wartość predefiniowana", + "entity-attribute":"Atrybut jednostki", + "value":"Wartość", + "source-entity-alias":"Alias jednostki źródłowej", + "source-entity-attribute":"Atrybut jednostki źródłowej" + }, + "widget-font":{ + "font-settings":"Ustawienia czcionki", + "font-family":"Rodzina czcionek", + "size":"Rozmiar", + "relative-font-size":"Względny rozmiar czcionki (procenty)", + "font-style":"Styl", + "font-style-normal":"Normalny", + "font-style-italic":"Kursywa", + "font-style-oblique":"Pochyły", + "font-weight":"Grubość", + "font-weight-normal":"Normalna", + "font-weight-bold":"Pogrubiona", + "font-weight-bolder":"Grubsza", + "font-weight-lighter":"Lżejsza", + "color":"Kolor", + "shadow-color":"Kolor cienia", + "preview":"Podgląd", + "line-height":"Wysokość wiersza", + "auto":"Automatyczne" + }, + "home":{ + "no-data-available":"Brak dostępnych danych" + }, + "system-info":{ + "cpu":"Procesor", + "ram":"RAM", + "disk":"Dysk", + "cpu-warning-text":"Wysokie zużycie procesora. Aby uniknąć awarii systemu, zoptymalizuj wydajność systemu.", + "cpu-critical-text":"Krytycznie wysokie zużycie procesora. Aby uniknąć awarii systemu, zoptymalizuj wydajność systemu.", + "ram-warning-text":"Niski zapas pamięci RAM. Aby uniknąć awarii systemu, zoptymalizuj wydajność systemu lub zwiększ rozmiar pamięci RAM.", + "ram-critical-text":"Krytycznie niski zapas pamięci RAM. Aby uniknąć awarii systemu, zoptymalizuj wydajność systemu lub zwiększ rozmiar pamięci RAM.", + "disk-warning-text":"Niskie miejsce na dysku. Aby uniknąć utraty danych, zwolnij miejsce lub zwiększ rozmiar dysku.", + "disk-critical-text":"Krytycznie niskie miejsce na dysku. Aby uniknąć utraty danych, zwolnij miejsce lub zwiększ rozmiar dysku." + }, + "cluster-info":{ + "service-id":"ID usługi", + "service-type":"Typ usługi", + "no-data":"Brak danych" + }, + "transport-messages":{ + "title":"Wiadomości transportowe", + "info":"Wszystkie wiadomości odebrane od urządzeń" + }, + "activity":{ + "title":"Aktywność" + }, + "documentation":{ + "title":"Dokumentacja", + "add-link":"Dodaj link", + "add-link-title":"Dodaj link do dokumentacji", + "name":"Nazwa", + "name-required":"Nazwa jest wymagana.", + "link":"Link", + "link-required":"Link jest wymagany.", + "columns":"Kolumny" + }, + "quick-links":{ + "title":"Szybkie linki", + "add-link":"Dodaj link", + "add-link-title":"Dodaj szybki link", + "quick-link":"Szybki link", + "quick-link-required":"Szybki link jest wymagany.", + "no-links-matching":"Nie znaleziono linków pasujących do '{{name}}'.", + "columns":"Kolumny" + }, + "recent-dashboards":{ + "title":"Dashboardy", + "last":"Ostatnio przeglądane", + "starred":"Ulubione", + "name":"Nazwa", + "last-viewed":"Ostatnio przeglądane", + "no-last-viewed-dashboards":"Nie oglądałeś jeszcze żadnych dashboardów" + }, + "configured-features":{ + "title":"Skonfigurowane funkcje", + "info":"Status funkcji wymagających konfiguracji", + "email-feature":"Email", + "sms-feature":"SMS", + "slack-feature":"Slack", + "oauth2-feature":"OAuth 2", + "2fa-feature":"2FA", + "feature-configured":"Funkcja jest skonfigurowana.\nKliknij, aby skonfigurować", + "feature-not-configured":"Funkcja nie jest skonfigurowana.\nKliknij, aby skonfigurować" + }, + "version-info":{ + "title":"Wersja", + "contact-us":"Skontaktuj się z nami", + "current-version":"Aktualna wersja", + "current":"Aktualna", + "available-version":"Dostępna wersja", + "available":"Dostępna", + "upgrade":"Aktualizuj", + "version-is-up-to-date":"Wersja jest aktualna" + }, + "usage-info":{ + "title":"Użycie", + "entities":"Encje", + "api-calls":"Wywołania API" + }, + "functions":{ + "title":"Funkcje", + "pe-feature-tooltip":"Tylko w ThingsBoard\nProfessional Edition", + "switch-to-pe":"Przełącz do PE", + "alarms":"Alarmy", + "dashboards":"Dashboards", + "entities-and-relations":"Encje i relacje", + "profiles":"Profile", + "advanced-features":"Zaawansowane funkcje", + "notification-center":"Centrum powiadomień", + "api-usage":"Użycie API", + "customers":"Klienci", + "customers-hierarchy":"Hierarchia klientów", + "roles-and-permissions":"Role i uprawnienia", + "groups":"Grupy", + "integrations":"Integracje", + "solution-templates":"Szablony rozwiązań", + "scheduler":"Harmonogram", + "white-labeling":"White-labeling" + }, + "devices":{ + "view-docs":"Zobacz dokumentację", + "inactive":"Nieaktywne", + "active":"Aktywne", + "total":"Razem" + }, + "alarms":{ + "critical":"Krytyczne", + "assigned-to-me":"Przypisane do mnie", + "total":"Razem" + }, + "getting-started":{ + "get-started":"Rozpocznij", + "finish":"Zakończ", + "done-welcome-title":"Witamy na pokładzie", + "done-welcome-text":"Świetnie sobie poradziłeś!", + "sys-admin":{ + "step1":{ + "title":"Utwórz Najemcę i Administratora Najemcy", + "content":"

Najemca to osoba lub organizacja, która posiada lub wytwarza urządzenia i zasoby. Najemca może mieć wielu administratorów najemców, klientów, urządzeń i zasobów.

Administrator najemcy może tworzyć i zarządzać urządzeniami, zasobami, klientami i pulpitami w ramach konta najemcy.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-create-tenant":"Jak utworzyć Najemcę i Administratora Najemcy" + }, + "step2":{ + "title":"Skonfiguruj funkcję: Serwer poczty", + "content":"

Konfiguracja serwera poczty jest niezbędna do aktywacji użytkowników, odzyskiwania hasła i dostarczania powiadomień o alarmach.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-configure-mail-server":"Jak skonfigurować serwer poczty" + }, + "step3":{ + "title":"Skonfiguruj funkcję: Dostawca SMS", + "content":"

Skonfiguruj dostawców SMS, aby informować klientów o alarmach za pomocą wiadomości SMS.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-configure-sms-provider":"Jak skonfigurować dostawcę SMS" + }, + "step4":{ + "title":"Skonfiguruj funkcję: 2FA", + "content":"

Popraw bezpieczeństwo kont platformy za pomocą uwierzytelniania dwuetapowego.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-configure-2fa":"Jak skonfigurować 2FA" + }, + "step5":{ + "title":"Skonfiguruj funkcję: OAuth 2", + "content":"

Uprość logowanie dla użytkowników najemcy i klientów za pomocą funkcji jednokrotnego logowania za pośrednictwem OAuth 2.0.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-configure-oauth2":"Jak skonfigurować OAuth 2" + }, + "step6":{ + "title":"Skonfiguruj funkcję: Slack", + "content":"

Rozprowadzaj powiadomienia do użytkowników najemców i klientów za pośrednictwem Slack zgodnie z zasadami powiadomień, które ustawisz.

Przeczytaj dokumentację, aby się dowiedzieć, jak to zrobić:

", + "how-to-configure-notifications":"Jak skonfigurować Slack" + } + }, + "tenant-admin":{ + "step1":{ + "title":"Utwórz urządzenie", + "content":"

Dodaj swoje pierwsze urządzenie do platformy za pomocą interfejsu użytkownika. Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-create-device":"Jak utworzyć urządzenie" + }, + "step2":{ + "title":"Podłącz urządzenie", + "content-before":"

Aby podłączyć urządzenie, potrzebujesz danych uwierzytelniających urządzenia. Zalecamy korzystanie z domyślnie generowanych danych uwierzytelniających, czyli token dostępu, zgodnie z tym przewodnikiem.

  • Przejdź do tabeli urządzeń
  • Kliknij w wiersz urządzenia, aby otworzyć szczegóły urządzenia
  • Naciśnij przycisk „Kopiuj token dostępu”

Użyj prostych poleceń, aby publikować dane za pomocą protokołu HTTP. Nie zapomnij zamienić $ACCESS_TOKEN na swój token dostępu do urządzenia:

", + "ubuntu":{ + "install-curl":"Zainstaluj cURL dla Ubuntu:" + }, + "macos":{ + "install-curl":"Zainstaluj cURL dla MacOS:" + }, + "windows":{ + "install-curl":"Od Windows 10 b17063 cURL jest dostępny domyślnie." + }, + "replace-access-token":"Zamień $ACCESS_TOKEN na token dostępu do swojego urządzenia:", + "content-after":"

Możesz także używać innych protokołów, takich jak MQTT, CoAP, itp.

Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-connect-device":"Jak podłączyć urządzenie" + }, + "step3":{ + "title":"Utwórz pulpit", + "content":"

Utwórz pulpit, aby wizualizować dane z jednostek, takich jak zasoby, urządzenia, itp.

Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-create-dashboard":"Jak utworzyć pulpit" + }, + "step4":{ + "title":"Skonfiguruj reguły alarmowe", + "alarm-rules":"Reguły alarmowe", + "content":"

Podnieśmy alarm, gdy temperatura osiągnie 25°C. Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-configure-alarm-rules":"Jak skonfigurować reguły alarmowe" + }, + "step5":{ + "title":"Utwórz alarm", + "content-before":"

Aby uruchomić alarm, prześlij nową wartość telemetrii równej lub wyższej niż 26°C.

", + "replace-access-token":"Zamień $ACCESS_TOKEN na token dostępu do swojego urządzenia:", + "content-after":"

Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-create-alarm":"Jak utworzyć alarm" + }, + "step6":{ + "title":"Utwórz klienta i przypisz pulpit", + "content":"

Tworząc pulpity dla końcowych użytkowników, użytkownik klienta może widzieć tylko swoje urządzenia, a dane innego klienta będą ukryte.

Przeczytaj dokumentację, aby dowiedzieć się, jak to zrobić:

", + "how-to-create-customer-and-assign-dashboard":"Jak utworzyć klienta i przypisać pulpit" + } + } + } + }, + "color":{ + "color":"Kolor" + }, + "icon":{ + "icon":"Ikona", + "icons":"Ikony", + "select-icon":"Wybierz ikonę", + "material-icons":"Ikony materiałowe", + "show-all":"Pokaż wszystkie ikony", + "search-icon":"Wyszukaj ikonę", + "no-icons-found":"Nie znaleziono ikon dla '{{iconSearch}}'" + }, + "phone-input":{ + "phone-input-label":"Numer telefonu", + "phone-input-required":"Numer telefonu jest wymagany", + "phone-input-validation":"Numer telefonu jest nieprawidłowy lub niemożliwy", + "phone-input-pattern":"Nieprawidłowy numer telefonu. Powinien być w formacie E.164, np. {{phoneNumber}}", + "phone-input-hint":"Numer telefonu w formacie E.164, np. {{phoneNumber}}" + }, + "custom":{ + "widget-action":{ + "action-cell-button":"Przycisk w komórce akcji", + "row-click":"Po kliknięciu w wiersz", + "polygon-click":"Po kliknięciu w wielokąt", + "marker-click":"Po kliknięciu w marker", + "circle-click":"Po kliknięciu w okrąg", + "tooltip-tag-action":"Akcja etykiety narzędziowej", + "node-selected":"Po wybraniu węzła", + "element-click":"Po kliknięciu w element HTML", + "pie-slice-click":"Po kliknięciu w kawałek tarty", + "row-double-click":"Po podwójnym kliknięciu w wiersz", + "card-click":"Po kliknięciu w kartę" + } + }, + "paginator":{ + "items-per-page":"Elementy na stronie:", + "first-page-label":"Pierwsza strona", + "last-page-label":"Ostatnia strona", + "next-page-label":"Następna strona", + "previous-page-label":"Poprzednia strona", + "items-per-page-separator":"z" + }, + "language":{ + "language":"Język" + } +} diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index 51c8579980..de9baa3998 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1897,7 +1897,7 @@ "action-source-required": "A fonte da ação é obrigatória.", "action-name": "Nome", "action-name-required": "O nome da ação é obrigatório!", - "action-name-not-unique": "Já existe outra ação com o mesmo nome.
O nome da ação na mesma fonte de ação deve ser exclusivo.", + "action-name-not-unique": "Já existe outra ação com o mesmo nome.\nO nome da ação na mesma fonte de ação deve ser exclusivo.", "action-icon": "Ícone", "action-type": "Tipo", "action-type-required": "O tipo de ação é obrigatório.", diff --git a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json index 67f8d839be..2eb5bc491a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -1648,7 +1648,7 @@ "action-source-required": "Sursa acțiunii este obligatorie", "action-name": "Numele Acțiunii", "action-name-required": "Numele acțiunii este obligatoriu", - "action-name-not-unique": "O acţiune cu acelaşi nume este deja definită
Numele definit al acțiunii trebuie să fie unic in aceeaşi sursă de date", + "action-name-not-unique": "O acţiune cu acelaşi nume este deja definită\nNumele definit al acțiunii trebuie să fie unic in aceeaşi sursă de date", "action-icon": "Pictogramă", "action-type": "Tipul", "action-type-required": "Tipul acțiunii este obligatoriu", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 4fded7d020..1948e22e2a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -2308,7 +2308,7 @@ "action-source-required": "Zahtevan je vir dejanj.", "action-name": "Ime", "action-name-required": "Ime dejanja je obvezno.", - "action-name-not-unique": "Še eno dejanje z istim imenom že obstaja.
Ime dejanja mora biti enolično v istem viru dejanj.", + "action-name-not-unique": "Še eno dejanje z istim imenom že obstaja. \n Ime dejanja mora biti enolično v istem viru dejanj.", "action-icon": "Ikona", "action-type": "Vrsta", "action-type-required": "Zahtevana je vrsta dejanja.", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index b5b70cd8f9..e516196b28 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -2951,7 +2951,7 @@ "action-source-required": "Eylem kaynağı gerekli.", "action-name": "İsim", "action-name-required": "Eylem ismi gerekli.", - "action-name-not-unique": "Aynı ada sahip başka bir işlem zaten var.
Eylem adı, aynı eylem kaynağı içinde emsalsiz olmalıdır.", + "action-name-not-unique": "Aynı ada sahip başka bir işlem zaten var.\nEylem adı, aynı eylem kaynağı içinde emsalsiz olmalıdır.", "action-icon": "İkon", "action-type": "Tür", "action-type-required": "Eylem türü gerekli.", diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index 9fda1518ef..e1bf787994 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -2390,7 +2390,7 @@ "action-source-required": "Необхідно вказати джерело дії.", "action-name": "Назва дії", "action-name-required": "Необхідно вказати назву дії.", - "action-name-not-unique": "Дія з такою назвою вже існує.
Назва дії має бути унікальною в межах одного джерела дії.", + "action-name-not-unique": "Дія з такою назвою вже існує.\nНазва дії має бути унікальною в межах одного джерела дії.", "action-icon": "Іконка", "action-type": "Тип", "action-type-required": "Необхідно вказати тип дії.", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 28c4d20f59..fdc0274cdf 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -77,7 +77,10 @@ "show-more": "显示更多", "dont-show-again": "不再显示", "see-documentation": "查看文档", - "clear": "清除" + "clear": "清除", + "upload": "上传", + "delete-anyway": "仍要删除", + "delete-selected": "删除所选" }, "aggregation": { "aggregation": "聚合", @@ -470,7 +473,12 @@ "notifications-settings": "通知设置", "slack-api-token": "Slack API令牌", "slack": "Slack", - "slack-settings": "Slack 设置" + "slack-settings": "Slack 设置", + "maximum-password-length": "密码最大长度", + "maximum-password-length-min": "密码最大长度应至少为6个字符", + "maximum-password-length-less-min": "密码最大长度应大于最小长度", + "force-reset-password-if-no-valid": "如果密码不可用则强制重置密码", + "force-reset-password-if-no-valid-hint": "启用此功能时请小心:它会要求使用不可用密码的用户通过电子邮件重置其密码。" }, "alarm": { "alarm": "告警", @@ -598,7 +606,7 @@ "filter-type-single-entity": "单个实体", "filter-type-entity-list": "实体列表", "filter-type-entity-name": "实体名称", - "filter-type-entity-type": "Entity type", + "filter-type-entity-type": "实体类型", "filter-type-state-entity": "仪表板状态实体", "filter-type-state-entity-description": "仪表板实体令牌状态参数", "filter-type-asset-type": "资产类型", @@ -1188,7 +1196,9 @@ "assign-dashboard-to-edge": "将仪表板分配给边缘", "assign-dashboard-to-edge-text": "请选择要分配给边缘的仪表板", "non-existent-dashboard-state-error": "找不到ID为 '{{ stateId }}' 的仪表板状态。", - "edit-mode": "编辑模式" + "edit-mode": "编辑模式", + "state-controller-default": "静态(已弃用)", + "duplicate-state-action": "复制状态" }, "datakey": { "settings": "设置", @@ -1202,13 +1212,13 @@ "data-generation-func": "数据生成功能", "use-data-post-processing-func": "使用数据后处理功能", "configuration": "数据键配置", - "timeseries": "Timeseries", + "timeseries": "时间序列", "attributes": "属性", "entity-field": "实体字段", "alarm": "告警字段", - "timeseries-required": "实体 Timeseries 必填。", - "timeseries-or-attributes-required": "实体 Timeseries/属性必填。", - "alarm-fields-timeseries-or-attributes-required": "告警字段或实体 Timeseries/属性必填。", + "timeseries-required": "实体时间序列必填。", + "timeseries-or-attributes-required": "实体时间序列/属性必填。", + "alarm-fields-timeseries-or-attributes-required": "告警字段或实体时间序列/属性必填。", "maximum-timeseries-or-attributes": "最多允许 { count, plural, =1 {1 个 timeseries/属性。} other {# 个 timeseries/属性。} }", "alarm-fields-required": "告警字段必填。", "function-types": "函数类型", @@ -1226,10 +1236,10 @@ "latest-key": "最新值数据键", "latest-key-functions": "最新值数据键函数", "latest-key-function": "最新值数据键函数", - "timeseries-keys": "Timeseries 数据键", - "timeseries-key": "Timeseries 数据键", - "timeseries-key-functions": "Timeseries数据键函数", - "timeseries-key-function": "Timeseries数据键函数", + "timeseries-keys": "时间序列数据键", + "timeseries-key": "时间序列数据键", + "timeseries-key-functions": "时间序列数据键函数", + "timeseries-key-function": "时间序列数据键函数", "maximum-function-types": "最多允许 { count, plural, =1 {1 个函数类型} other {# 个函数类型} }", "time-description": "当前值的时间戳;", "value-description": "当前值;", @@ -1578,9 +1588,9 @@ "mqtt-send-ack-on-validation-exception": "发布消息验证失败时发送PUBACK", "mqtt-send-ack-on-validation-exception-hint": "默认情况下平台将关闭相关消息验证失败的MQTT会话,启用后平台将发布确认而不是关闭会话。", "snmp-add-mapping": "添加SNMP映射", - "snmp-mapping-not-configured": "OID到Timeseries/遥测的映射未配置", - "snmp-timseries-or-attribute-name": "用于映射的Timeseries/属性名称", - "snmp-timseries-or-attribute-type": "用于映射的Timeseries/属性类型", + "snmp-mapping-not-configured": "OID到时间序列/遥测的映射未配置", + "snmp-timseries-or-attribute-name": "用于映射的时间序列/属性名称", + "snmp-timseries-or-attribute-type": "用于映射的时间序列/属性类型", "snmp-method-pdu-type-get-request": "GetRequest", "snmp-method-pdu-type-get-next-request": "GetNextRequest", "snmp-oid": "OID", @@ -2017,7 +2027,8 @@ "sync-process-started-successfully": "同步处理开始成功!", "missing-related-rule-chains-title": "边缘缺少关联规则链", "missing-related-rule-chains-text": "分配给边缘的规则链使用规则节点将消息转发给未分配给当前边缘的规则链。

缺少的规则链列表:
{{missingRuleChains}}", - "widget-datasource-error": "组件只支持边缘实体数据源" + "widget-datasource-error": "组件只支持边缘实体数据源", + "upgrade-instructions": "升级说明" }, "edge-event": { "type-dashboard": "仪表板", @@ -2046,7 +2057,7 @@ "action-type-post-attributes": "推送属性", "action-type-attributes-updated": "属性更新", "action-type-attributes-deleted": "属性删除", - "action-type-timeseries-updated": "时序更新", + "action-type-timeseries-updated": "时间序列更新", "action-type-credentials-updated": "认证更新", "action-type-assigned-to-customer": "分配给客户", "action-type-unassigned-from-customer": "取消分配客户", @@ -2211,7 +2222,9 @@ "type-notification-request": "通知请求", "type-notification-template": "通知模板", "type-notification-templates": "通知模板", - "list-of-notification-templates": "{ count, plural, =1 {1 个通知模板} other {# 个通知模板} }" + "list-of-notification-templates": "{ count, plural, =1 {1 个通知模板} other {# 个通知模板} }", + "type-tb-resources": "资源", + "list-of-tb-resources": "{ count, plural, =1 {1 个资源} other {# 个资源} }" }, "entity-field": { "created-time": "创建时间", @@ -2328,7 +2341,7 @@ "attributes-propagation": "属性传播", "attributes-propagation-hint": "每次保存或更新这个实体视图时,实体视图将自动从目标实体复制指定的属性。由于性能原因,目标实体属性不会在每次属性更改时传递到实体视图。您可以通过配置\"copy to view\"规则链中的规则节点,并将\"Post attributes\"和\"attributes Updated\"消息链接到新规则节点,从而启用自动传递。", "timeseries-data": "时间序列数据", - "timeseries-data-hint": "配置目标实体的 Timeseries 数据键,以便实体视图可以访问这些键。此 Timeseries 数据是只读的。", + "timeseries-data-hint": "配置目标实体的时间序列数据键,以便实体视图可以访问这些键。此时间序列数据是只读的。", "search": "查找实体视图", "selected-entity-views": "已选择 { count, plural, =1 {1 个实体视图} other {# 个实体视图} }", "make-public-entity-view-title": "确定要将实体视图 '{{entityViewName}}' 设为公开吗?", @@ -2427,8 +2440,8 @@ "attributes": "属性", "add-attribute": "添加属性", "add-map": "添加映射元素", - "timeseries": "Timeseries", - "add-timeseries": "添加 Timeseries", + "timeseries": "时间序列", + "add-timeseries": "添加时间序列", "field-required": "必填字段", "brokers": "代理服务器组", "add-broker": "添加代理服务器", @@ -2523,7 +2536,7 @@ "modbus-device-name": "设备名称", "modbus-poll-period": "轮询周期 (毫秒)", "modbus-attributes-poll-period": "轮询属性周期 (毫秒)", - "modbus-timeseries-poll-period": "Timeseries 轮询周期 (毫秒)", + "modbus-timeseries-poll-period": "时间序列数据轮询周期 (毫秒)", "modbus-poll-period-range": "轮询周期应为正值。", "modbus-tag": "标签", "modbus-function": "函数", @@ -2613,7 +2626,7 @@ "key-type": { "key-type": "键类型", "attribute": "属性", - "timeseries": "Timeseries", + "timeseries": "时间序列", "entity-field": "实体", "constant": "常量", "client-attribute": "客户端属性", @@ -2908,7 +2921,13 @@ "grpc-max-pings-without-data": "在没有接收到任何数据之前,服务器可以发送的keepalive ping消息的最大数量,然后将连接视为死亡", "grpc-min-ping-interval-without-data": "在没有发送或接收数据时,服务器在发送keepalive ping消息之间应等待的最小时间量", "permit-without-calls": "允许服务器在没有活动RPC调用时保持GRPC连接活动" - } + }, + "docker-label": "使用以下指令在 Docker compose 中运行 IoT 网关,并为选定的设备提供凭据", + "install-docker-compose": "使用以下说明下载、安装和设置 Docker Compose", + "download-configuration-file": "下载配置文件", + "download-docker-compose": "下载您的网关的 docker-compose.yml 文件", + "launch-gateway": "启动网关", + "launch-docker-compose": "在包含 docker-compose.yml 文件的文件夹中,使用以下命令在终端中启动网关" }, "grid": { "delete-item-title": "确定要删除此项吗?", @@ -2939,6 +2958,59 @@ "browse-file": "浏览文件", "browse-files": "浏览文件" }, + "image": { + "gallery": "图像库", + "search": "搜索图像", + "selected-images": "已选择 { count, plural, =1 {1 个图像} other {# 个图像} }", + "created-time": "创建时间", + "name": "名称", + "name-required": "名称不能为空。", + "resolution": "分辨率", + "size": "大小", + "system": "系统", + "download-image": "下载图像", + "export-image": "导出图像为JSON", + "import-image": "从JSON导入图像", + "upload-image": "上传图像", + "edit-image": "编辑图像", + "image-details": "图像详情", + "no-images": "未找到图像", + "delete-image": "删除图像", + "delete-image-title": "确定要删除图像 '{{imageTitle}}' 吗?", + "delete-image-text": "请注意,确认后图像将无法恢复。", + "delete-images-title": "确定要删除 { count, plural, =1 {1 个图像} other {# 个图像} } 吗?", + "delete-images-text": "请注意,确认后所有选定的图像都将被删除,并且所有相关数据将无法恢复。", + "list-mode": "列表视图", + "grid-mode": "网格视图", + "image-preview": "图像预览", + "update-image": "更新图像", + "export-failed-error": "无法导出图像:{{error}}", + "image-json-file": "图像JSON文件", + "invalid-image-json-file-error": "无法从JSON导入图像:无效的图像JSON数据结构。", + "image-is-in-use": "图像被其他实体使用", + "images-are-in-use": "图像被其他实体使用", + "image-is-in-use-text": "无法删除图像'{{title}}',因为它被以下实体使用:", + "images-are-in-use-text": "由于图像被其他实体使用,无法删除所有图像。
您可以通过单击相应图像行上的引用按钮查看引用的实体。
如果仍然要删除这些图像,请在下方的表格中选择它们,然后点击删除所选按钮。", + "delete-image-in-use-text": "如果仍然要删除该图像,请点击无论如何删除按钮。", + "system-entities": "系统实体:", + "entities": "实体:", + "references": "引用", + "include-system-images": "包含系统图像", + "clear-image": "清除图像", + "no-image": "无图像", + "no-image-selected": "未选择图像", + "browse-from-gallery": "从图像库浏览", + "set-link": "设置链接", + "image-link": "图像链接", + "link": "链接", + "copy-image-link": "复制图像链接", + "embed-image": "嵌入图像", + "embed-to-html": "嵌入到HTML", + "embed-to-html-hint": "此功能将使链接对任何未经授权的用户可用。", + "embed-to-html-text": "使用以下代码片段,您可以将图像嵌入到基于纯HTML的组件中。
此类组件包括HTML卡片小部件、单元格内容函数等。", + "embed-to-angular-template": "嵌入到Angular HTML模板", + "embed-to-angular-template-text": "使用以下代码片段,您可以将图像嵌入到Angular HTML模板中。
此类组件包括Markdown小部件、小部件编辑器中的HTML部分、自定义操作等。" + }, "image-input": { "drop-images-or": "拖放一张或多张图片", "drag-and-drop": "拖放", @@ -2972,7 +3044,7 @@ "client-attribute": "客户端属性", "shared-attribute": "共享属性", "server-attribute": "服务器属性", - "timeseries": "Timeseries", + "timeseries": "时间序列", "entity-field": "实体字段", "access-token": "访问令牌", "x509": "X.509", @@ -3192,8 +3264,8 @@ "email-preview": "Email 通知预览", "slack": "Slack", "slack-preview": "Slack 通知预览", - "microsoft-teams" : "Microsoft Teams", - "microsoft-teams-preview" : "Microsoft Teams 通知预览", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Microsoft Teams 通知预览", "sms": "SMS", "sms-preview": "SMS 通知预览", "web": "Web", @@ -3459,7 +3531,8 @@ "version-tag": "版本标签", "version-tag-hint": "自定义标签应与您设备报告的软件包版本相匹配。", "version-max-length": "版本长度应该少于256个字符", - "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置和包类型。" + "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置和包类型。", + "package-file": "包文件" }, "position": { "top": "顶部", @@ -3557,7 +3630,8 @@ "password-requirements": "密码要求", "password-should-difference": "新密码应与当前密码不同", "special-character": "{ count, plural, =1 {1 位特殊字符} other {# 位特殊字符} }", - "uppercase-letter": "{ count, plural, =1 {1 位大写字母} other {# 位大写字母} }" + "uppercase-letter": "{ count, plural, =1 {1 位大写字母} other {# 位大写字母} }", + "at-most": "最多:" } }, "relation": { @@ -3640,7 +3714,9 @@ "js-module": "JS 模块", "lwm2m-model": "LWM2M 模型", "pkcs-12": "PKCS #12" - } + }, + "resource-file": "资源文件", + "resource-files": "资源文件" }, "rulechain": { "rulechain": "规则链", @@ -4104,7 +4180,10 @@ "transport-device-telemetry-msg": "传输设备遥测消息", "transport-device-telemetry-data-points": "传输设备遥测数据点", "sec": "秒" - } + }, + "maximum-resource-size": "最大资源文件大小(字节)", + "maximum-resource-size-required": "最大资源文件大小是必需的", + "maximum-resource-size-range": "最大资源文件大小不能为负数" }, "timeinterval": { "seconds-interval": "{ seconds, plural, =1 {1 秒} other {# 秒} }", @@ -4205,409 +4284,6 @@ "background-color": "背景颜色", "background-blur": "背景模糊" }, - "unit": { - "millimeter": "mm", - "centimeter": "cm", - "angstrom": "Å", - "nanometer": "nm", - "micrometer": "μm", - "meter": "m", - "kilometer": "km", - "inch": "in", - "foot": "ft", - "yard": "yd", - "mile": "mi", - "nautical-mile": "nmi", - "astronomical-unit": "au", - "reciprocal-metre": "m⁻¹", - "meter-per-meter": "m/m", - "steradian": "sr", - "thou": "th", - "barleycorn": "bc", - "hand": "hd", - "chain": "ch", - "furlong": "fur", - "league": "lea", - "fathom": "fath", - "cable": "cb", - "link": "li", - "rod": "rd", - "nanogram": "ng", - "microgram": "μg", - "milligram": "mg", - "gram": "g", - "kilogram": "kg", - "tonne": "t", - "ounce": "oz", - "pound": "lb", - "stone": "st", - "hundredweight-count": "cwt", - "short-tons": "s.t.", - "dalton": "Da", - "grain": "gr", - "drachm": "dr", - "quarter": "qr", - "slug": "slug", - "carat": "ct", - "cubic-millimeter": "mm³", - "cubic-centimeter": "cm³", - "cubic-meter": "m³", - "cubic-kilometer": "km³", - "microliter": "μL", - "milliliter": "mL", - "liter": "L", - "hectoliter": "hL", - "cubic-inch": "in³", - "cubic-foot": "ft³", - "cubic-yard": "yd³", - "fluid-ounce": "fl oz", - "pint": "pt", - "quart": "qt", - "gallon": "gal", - "oil-barrels": "bbl", - "cubic-meter-per-kilogram": "m³/kg", - "gill": "gi", - "hogshead": "hhd", - "teaspoon": "tsp", - "tablespoon": "tbsp", - "cup": "cup", - "celsius": "°C", - "kelvin": "K", - "rankine": "°R", - "fahrenheit": "°F", - "percent": "%", - "meter-per-second": "m/s", - "kilometer-per-hour": "km/h", - "foot-per-second": "ft/s", - "mile-per-hour": "mph", - "knot": "kn", - "millimeters-per-minute": "mm/min", - "kilometer-per-hour-squared": "km/h²", - "foot-per-second-squared": "ft/s²", - "pascal": "Pa", - "kilopascal": "kPa", - "megapascal": "MPa", - "gigapascal": "GPa", - "millibar": "mbar", - "bar": "bar", - "kilobar": "kbar", - "newton": "N", - "newton-meter": "N·m", - "foot-pounds": "ft·lbf", - "inch-pounds": "in·lbf", - "newton-per-meter": "N/m", - "atmospheres": "atm", - "pounds-per-square-inch": "psi", - "torr": "Torr", - "inches-of-mercury": "inHg", - "pascal-per-square-meter": "Pa/m²", - "pound-per-square-inch": "psi", - "newton-per-square-meter": "N/m²", - "kilogram-force-per-square-meter": "kgf/m²", - "pascal-per-square-centimeter": "Pa/cm²", - "ton-force-per-square-inch": "tonf/in²", - "kilonewton-per-square-meter": "kN/m²", - "newton-per-square-millimeter": "N/mm²", - "microjoule": "μJ", - "millijoule": "mJ", - "joule": "J", - "kilojoule": "kJ", - "megajoule": "MJ", - "gigajoule": "GJ", - "watt-hour": "Wh", - "kilowatt-hour": "kWh", - "electron-volts": "eV", - "joules-per-coulomb": "J/C", - "british-thermal-unit": "BTU", - "foot-pound": "ft·lb", - "calorie": "cal", - "small-calorie": "cal", - "kilocalorie": "kcal", - "joule-per-kelvin": "J/K", - "joule-per-kilogram-kelvin": "J/(kg·K)", - "joule-per-kilogram": "J/kg", - "watt-per-meter-kelvin": "W/(m·K)", - "joule-per-cubic-meter": "J/m³", - "therm": "thm", - "electric-dipole-moment": "Debye", - "magnetic-dipole-moment": "Am²", - "debye": "D", - "coulomb-per-square-meter-per-volt": "C/(m²·V)", - "milliwatt": "mW", - "microwatt": "μW", - "watt": "W", - "kilowatt": "kW", - "megawatt": "MW", - "gigawatt": "GW", - "metric-horsepower": "PS", - "milliwatt-per-square-centimeter": "mW/cm²", - "watt-per-square-centimeter": "W/cm²", - "kilowatt-per-square-centimeter": "kW/cm²", - "milliwatt-per-square-meter": "mW/m²", - "watt-per-square-meter": "W/m²", - "kilowatt-per-square-meter": "kW/m²", - "watt-per-square-inch": "W/in²", - "kilowatt-per-square-inch": "kW/in²", - "horsepower": "hp", - "btu-per-hour": "BTU/h", - "coulomb": "C", - "millicoulomb": "mC", - "microcoulomb": "μC", - "picocoulomb": "pC", - "coulomb-per-meter": "C/m", - "coulomb-per-cubic-meter": "C/m³", - "coulomb-per-square-meter": "C/m²", - "square-millimeter": "mm²", - "square-centimeter": "cm²", - "square-meter": "m²", - "hectare": "ha", - "square-kilometer": "km²", - "square-inch": "in²", - "square-foot": "ft²", - "square-yard": "yd²", - "acre": "ac", - "square-mile": "mi²", - "are": "a", - "barn": "b", - "circular-inch": "c in²", - "milliampere-hour": "mAh", - "ampere-hours": "Ah", - "kiloampere-hours": "kAh", - "nanoampere": "nA", - "picoampere": "pA", - "microampere": "μA", - "milliampere": "mA", - "ampere": "A", - "kiloamperes": "kA", - "microampere-per-square-centimeter": "μA/cm²", - "ampere-per-square-meter": "A/m²", - "ampere-per-meter": "A/m", - "oersted": "Oe", - "bohr-magneton": "μB", - "ampere-meter-squared": "A·m²", - "ampere-meter": "A·m", - "nanovolt": "nV", - "picovolt": "pV", - "millivolts": "mV", - "microvolts": "μV", - "volt": "V", - "kilovolts": "kV", - "dbmV": "dBmV", - "dbm": "dBm", - "volt-meter": "V·m", - "kilovolt-meter": "kV·m", - "megavolt-meter": "MV·m", - "microvolt-meter": "μV·m", - "millivolt-meter": "mV·m", - "nanovolt-meter": "nV·m", - "ohm": "Ω", - "microohm": "μΩ", - "milliohm": "mΩ", - "kilohm": "kΩ", - "megohm": "MΩ", - "gigohm": "GΩ", - "hertz": "Hz", - "kilohertz": "kHz", - "megahertz": "MHz", - "gigahertz": "GHz", - "rpm": "rpm", - "candela-per-square-meter": "cd/m²", - "candela": "cd", - "lumen": "lm", - "lux": "lx", - "foot-candle": "fc", - "lumen-per-square-meter": "lm/m²", - "lux-second": "lx·s", - "lumen-second": "lm·s", - "lumens-per-watt": "lm/W", - "absorbance": "AU", - "mole": "mol", - "nanomole": "nmol", - "micromole": "μmol", - "millimole": "mmol", - "kilomole": "kmol", - "mole-per-cubic-meter": "mol/m³", - "rssi": "RSSI", - "ppm": "ppm", - "ppb": "ppb", - "micrograms-per-cubic-meter": "μg/m³", - "aqi": "AQI", - "gram-per-cubic-meter": "g/m³", - "gram-per-kilogram": "g/kg", - "millimeters-per-second": "mm/s", - "neper": "Np", - "bel": "B", - "decibel": "dB", - "meters-per-second-squared": "m/s²", - "becquerel": "Bq", - "curie": "Ci", - "gray": "Gy", - "sievert": "Sv", - "roentgen": "R", - "cps": "cps", - "rad": "rad", - "rem": "rem", - "dps": "dps", - "rutherford": "Rd", - "coulombs-per-kilogram": "C/kg", - "becquerels-per-cubic-meter": "Bq/m³", - "curies-per-liter": "Ci/L", - "becquerels-per-second": "Bq/s", - "curies-per-second": "Ci/s", - "gy-per-second": "Gy/s", - "watt-per-steradian": "W/sr", - "watt-per-square-metre-steradian": "W/(m²·sr)", - "ph-level": "pH", - "turbidity": "NTU", - "mg-per-liter": "mg/L", - "microsiemens-per-centimeter": "μS/cm", - "millisiemens-per-meter": "mS/m", - "siemens-per-meter": "S/m", - "kilogram-per-cubic-meter": "kg/m³", - "gram-per-cubic-centimeter": "g/cm³", - "kilogram-per-square-meter": "kg/m²", - "milligram-per-milliliter": "mg/mL", - "pound-per-cubic-foot": "lb/ft³", - "ounces-per-cubic-inch": "oz/in³", - "tons-per-cubic-yard": "ton/yd³", - "particle-density": "PD", - "kilometers-per-liter": "km/L", - "miles-per-gallon": "mpg", - "liters-per-100-km": "L/100 km", - "gallons-per-mile": "gal/mi", - "liters-per-hour": "L/h", - "gallons-per-hour": "gal/h", - "beats-per-minute": "bpm", - "millimeters-of-mercury": "mmHg", - "milligrams-per-deciliter": "mg/dL", - "g-force": "G", - "kilonewton": "kN", - "kilogram-force": "kgf", - "pound-force": "lbf", - "kilopound-force": "kip", - "dyne": "dyn", - "poundal": "pdl", - "kip": "kip", - "gal": "Gal", - "gravity": "g₀", - "hectopascal": "hPa", - "atmosphere": "atm", - "millibars": "mbar", - "inch-of-mercury": "inHg", - "richter-scale": "Richter", - "second": "s", - "minute": "min", - "hour": "hr", - "day": "day", - "week": "wk", - "month": "mo", - "year": "yr", - "cubic-foot-per-minute": "ft³/min", - "cubic-meters-per-hour": "m³/h", - "cubic-meters-per-second": "m³/s", - "liter-per-second": "L/s", - "liter-per-minute": "L/min", - "gallons-per-minute": "GPM", - "cubic-foot-per-second": "ft³/s", - "milliliters-per-minute": "mL/min", - "bit": "bit", - "byte": "B", - "kilobyte": "KB", - "megabyte": "MB", - "gigabyte": "GB", - "terabyte": "TB", - "petabyte": "PB", - "exabyte": "EB", - "zettabyte": "ZB", - "yottabyte": "YB", - "bit-per-second": "bps", - "kilobit-per-second": "Kbps", - "megabit-per-second": "Mbps", - "gigabit-per-second": "Gbps", - "terabit-per-second": "Tbps", - "byte-per-second": "B/s", - "kilobyte-per-second": "KB/s", - "megabyte-per-second": "MB/s", - "gigabyte-per-second": "GB/s", - "degree": "°", - "radian": "rad", - "gradian": "grad", - "mil": "mil", - "revolution": "rev", - "siemens": "S", - "millisiemens": "mS", - "microsiemens": "μS", - "kilosiemens": "kS", - "megasiemens": "MS", - "gigasiemens": "GS", - "farad": "F", - "millifarad": "mF", - "microfarad": "μF", - "nanofarad": "nF", - "picofarad": "pF", - "kilofarad": "kF", - "megafarad": "MF", - "gigafarad": "GF", - "terfarad": "TF", - "farad-per-meter": "F/m", - "tesla": "T", - "gauss": "G", - "kilogauss": "kG", - "millitesla": "mT", - "microtesla": "μT", - "nanotesla": "nT", - "kilotesla": "kT", - "megatesla": "MT", - "millitesla-square-meters": "mT·m²", - "gamma": "γ", - "lambda": "λ", - "square-meter-per-second": "m²/s", - "square-centimeter-per-second": "cm²/s", - "stoke": "St", - "centistokes": "cSt", - "square-foot-per-second": "ft²/s", - "square-inch-per-second": "in²/s", - "pascal-second": "Pa·s", - "centipoise": "cP", - "poise": "P", - "reynolds": "Re", - "pound-per-foot-hour": "lb/(ft·hr)", - "newton-second-per-square-meter": "N·s/m²", - "dyne-second-per-square-centimeter": "dyn·s/cm²", - "kilogram-per-meter-second": "kg/(m·s)", - "tesla-square-meters": "T·m²", - "maxwell": "Mx", - "tesla-per-meter": "T/m", - "gauss-per-centimeter": "G/cm", - "weber": "Wb", - "microweber": "μWb", - "milliweber": "mWb", - "gauss-square-centimeter": "G·cm²", - "kilogauss-square-centimeter": "kG·cm²", - "henry": "H", - "millihenry": "mH", - "microhenry": "μH", - "nanohenry": "nH", - "henry-per-meter": "H/m", - "tesla-meter-per-ampere": "T·m/A", - "gauss-per-oersted": "G/Oe", - "kilogram-per-mole": "kg/mol", - "gram-per-mole": "g/mol", - "milligram-per-mole": "mg/mol", - "joule-per-mole": "J/mol", - "joule-per-mole-kelvin": "J/mol-K", - "millivolts-per-meter": "mV/m", - "volts-per-meter": "V/m", - "kilovolts-per-meter": "kV/m", - "radian-per-second": "rad/s", - "radian-per-second-squared": "rad/s^2", - "revolutions-per-minute-per-second": "rpm/s", - "revolutions-per-minute-per-second-squared": "rpm/s^2", - "deg-per-second": "deg/s", - "degrees-brix": "°Bx", - "katal": "kat", - "katal-per-cubic-metre": "kat/m^3" - }, "user": { "user": "用户", "users": "用户", @@ -4765,15 +4441,15 @@ "no-widgets-text": "未找到部件", "management": "管理部件", "editor": "部件编辑器", - "confirm-to-exit-editor-html": "You have unsaved widget settings.
Are you sure you want to leave this page?", + "confirm-to-exit-editor-html": "有未保存的部件设置。
确定要离开此页面吗?", "widget-type-not-found": "加载部件配置出错。
可能关联的部件已经删除了。", "widget-type-load-error": "由于以下错误未加载部件:", "remove": "删除部件", - "delete": "Delete widget", + "delete": "删除部件", "edit": "编辑部件", "remove-widget-title": "确定要删除 '{{widgetTitle}}'部件吗?", "remove-widget-text": "确认后,控件和所有相关数据将变得不可恢复。", - "timeseries": "Timeseries", + "timeseries": "时间序列", "search-data": "查找数据", "no-data-found": "未找到数据", "latest": "最新值", @@ -4894,8 +4570,8 @@ "popover-placement-leftBottom": "左下", "popover-hide-on-click-outside": "在点击弹出框外部时隐藏弹出框", "popover-hide-dashboard-toolbar": "在弹出框中隐藏仪表板工具栏", - "popover-width": "弹出框的宽度使用浏览器单位表示(例如:100px、25vw)", - "popover-height": "弹出框的高度使用浏览器单位表示(例如:100px、25vh)", + "popover-width": "弹出宽度", + "popover-height": "弹出高度", "popover-style": "弹出框样式", "open-new-browser-tab": "在新的浏览器选项卡中打开", "mobile": { @@ -5008,7 +4684,7 @@ "action-source-required": "动作源必填", "action-name": "名称", "action-name-required": "动作名称必填。", - "action-name-not-unique": "动作名称已经存在。
相同动作源的动作名称必须唯一。", + "action-name-not-unique": "动作名称已经存在。\n相同动作源的动作名称必须唯一。", "action-icon": "图标", "show-hide-action-using-function": "使用函数显示/隐藏动作", "action-type": "类型", @@ -5148,12 +4824,12 @@ "axis-position-top": "顶部 (默认)", "axis-position-bottom": "底部", "custom-legend-settings": "自定义图例设置", - "enable-custom-legend": "启用自定义图例 (这将允许您在键标签中使用属性/Timeseries值)", + "enable-custom-legend": "启用自定义图例 (这将允许您在键标签中使用属性/时间序列值)", "key-name": "键名", "key-name-required": "键名是必需的", "key-type": "键类型", "key-type-attribute": "属性", - "key-type-timeseries": "Timeseries", + "key-type-timeseries": "时间序列", "label-keys-list": "要在标签中使用的键列表", "no-label-keys": "未配置键", "add-label-key": "添加新键", @@ -5552,7 +5228,7 @@ "discard-changes": "放弃更改", "entity-attribute-required": "实体属性必填", "entity-coordinate-required": "纬度和经度两个字段都是必需的", - "entity-timeseries-required": "实体 Timeseries 必填", + "entity-timeseries-required": "实体时间序列必填", "get-location": "获取当前位置", "invalid-date": "无效日期", "latitude": "纬度", @@ -5570,19 +5246,19 @@ "enable-https-use-widget": "请启用HTTPS以使用此部件", "no-found-your-camera": "未找到摄像机", "no-permission-camera": "权限被用户拒绝/此站点无权使用摄像机", - "no-timeseries-selected": "未选择 Timeseries", + "no-timeseries-selected": "未选择时间序列值", "secret-key": "密钥", "secret-key-required": "密钥必填", "switch-attribute-value": "切换实体属性值", "switch-camera": "切换摄像机", - "switch-timeseries-value": "切换实体 Timeseries 值", + "switch-timeseries-value": "切换实体时间序列值", "take-photo": "拍照", "time": "时间", - "timeseries-not-allowed": "Timeseries 参数不能用于此部件", + "timeseries-not-allowed": "时间序列参数不能用于此部件", "update-failed": "更新失败", "update-successful": "更新成功", "update-attribute": "更新属性", - "update-timeseries": "更新 Timeseries", + "update-timeseries": "更新时间序列", "value": "数值", "general-settings": "通用设置", "widget-title": "部件标题", @@ -5638,7 +5314,7 @@ "attribute-settings": "属性设置", "widget-mode": "部件模式", "widget-mode-update-attribute": "更新属性", - "widget-mode-update-timeseries": "更新 Timeseries", + "widget-mode-update-timeseries": "更新时间序列", "attribute-scope": "属性范围", "attribute-scope-server": "服务器属性", "attribute-scope-shared": "共享属性", @@ -5669,7 +5345,7 @@ "datakey-type": "数据键类型", "datakey-type-server": "服务器属性(默认)", "datakey-type-shared": "共享属性", - "datakey-type-timeseries": "Timeseries", + "datakey-type-timeseries": "时间序列", "datakey-value-type": "数据键值类型", "datakey-value-type-string": "字符串", "datakey-value-type-double": "双精度", @@ -5827,7 +5503,7 @@ "retrieve-value-method-attribute": "订阅属性获取值", "retrieve-value-method-timeseries": "订阅时间序列获取值", "attribute-value-key": "属性键", - "timeseries-value-key": "Timeseries键", + "timeseries-value-key": "时间序列键", "get-value-method": "RPC获取值方法", "parse-value-function": "解析值的函数", "update-value-settings": "更新值设置", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index 5b415558a1..eafc22498f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -3439,8 +3439,8 @@ "popover-placement-leftBottom": "左側底部", "popover-hide-on-click-outside": "在外部單擊時隱藏彈出視窗", "popover-hide-dashboard-toolbar": "在彈出提示框中隱藏對話工具欄", - "popover-width": "瀏覽器單元中的彈出框寬度(例如100px、25vw)", - "popover-height": "瀏覽器單元中的彈出框高度 (例如100px、25vh)", + "popover-width": "彈出寬度", + "popover-height": "彈出高度", "popover-style": "彈出提示框形式", "open-new-browser-tab": "在新的瀏覽器選項中打開", "mobile": { @@ -3535,7 +3535,7 @@ "action-source-required": "動作源必填", "action-name": "動作名稱", "action-name-required": "動作名稱必填。", - "action-name-not-unique": "動作名稱已經存在。
統一動作源的動作名稱必須唯一。", + "action-name-not-unique": "動作名稱已經存在。\n統一動作源的動作名稱必須唯一。", "action-icon": "圖示", "show-hide-action-using-function": "使用函數顯示/隱藏動作", "action-type": "類型", diff --git a/ui-ngx/src/assets/widget/button/basic.svg b/ui-ngx/src/assets/widget/button/basic.svg new file mode 100644 index 0000000000..6b005e345e --- /dev/null +++ b/ui-ngx/src/assets/widget/button/basic.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/button/filled.svg b/ui-ngx/src/assets/widget/button/filled.svg new file mode 100644 index 0000000000..c17871583a --- /dev/null +++ b/ui-ngx/src/assets/widget/button/filled.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/button/outlined.svg b/ui-ngx/src/assets/widget/button/outlined.svg new file mode 100644 index 0000000000..63b2232060 --- /dev/null +++ b/ui-ngx/src/assets/widget/button/outlined.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/button/underlined.svg b/ui-ngx/src/assets/widget/button/underlined.svg new file mode 100644 index 0000000000..6638662a21 --- /dev/null +++ b/ui-ngx/src/assets/widget/button/underlined.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/default-layout.svg b/ui-ngx/src/assets/widget/power-button/default-layout.svg new file mode 100644 index 0000000000..74210bff64 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/default-layout.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/default-volume-layout.svg b/ui-ngx/src/assets/widget/power-button/default-volume-layout.svg new file mode 100644 index 0000000000..9a502cc8f0 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/default-volume-layout.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/outlined-layout.svg b/ui-ngx/src/assets/widget/power-button/outlined-layout.svg new file mode 100644 index 0000000000..9510d68721 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/outlined-layout.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/outlined-volume-layout.svg b/ui-ngx/src/assets/widget/power-button/outlined-volume-layout.svg new file mode 100644 index 0000000000..9e511892b3 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/outlined-volume-layout.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/simplified-layout.svg b/ui-ngx/src/assets/widget/power-button/simplified-layout.svg new file mode 100644 index 0000000000..eecb1e9cbe --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/simplified-layout.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/simplified-volume-layout.svg b/ui-ngx/src/assets/widget/power-button/simplified-volume-layout.svg new file mode 100644 index 0000000000..500d9a0c97 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/simplified-volume-layout.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/slider/default-layout.svg b/ui-ngx/src/assets/widget/slider/default-layout.svg new file mode 100644 index 0000000000..0b6d1bce33 --- /dev/null +++ b/ui-ngx/src/assets/widget/slider/default-layout.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/slider/extended-layout.svg b/ui-ngx/src/assets/widget/slider/extended-layout.svg new file mode 100644 index 0000000000..923ee8af3d --- /dev/null +++ b/ui-ngx/src/assets/widget/slider/extended-layout.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/slider/simplified-layout.svg b/ui-ngx/src/assets/widget/slider/simplified-layout.svg new file mode 100644 index 0000000000..d2e3936db0 --- /dev/null +++ b/ui-ngx/src/assets/widget/slider/simplified-layout.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 67f8c6b641..ab018a8bc6 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -132,14 +132,14 @@ } } } - .tb-json-object-panel, .tb-css-content-panel { - margin: 0 0 8px; - } } .mat-expansion-panel-content { font: inherit; } } + .tb-json-object-panel, .tb-css-content-panel { + margin: 0 0 8px; + } } .tb-form-panel-title { @@ -233,6 +233,36 @@ } } + .tb-flex { + display: flex; + flex: 1; + gap: 8px; + &.row { + flex-direction: row; + } + &.column { + flex-direction: column; + } + &.flex-start { + justify-content: flex-start; + } + &.flex-end { + justify-content: flex-end; + } + &.space-between { + justify-content: space-between; + } + &.align-center { + align-items: center; + } + &.no-gap { + gap: 0; + } + &.fill-width { + width: 100%; + } + } + .tb-form-panel, .tb-form-row { .mat-slide { margin: 0; @@ -607,6 +637,14 @@ } } + button.mat-mdc-button-base.tb-nowrap { + .mdc-button__label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + .mat-mdc-chip-listbox.center-stretch { .mat-mdc-standard-chip { flex: 1; diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 0750de263f..8ca9fe4e37 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -1252,4 +1252,8 @@ mat-icon { .cursor-pointer { cursor: pointer; } + + .no-wrap { + white-space: nowrap; + } } diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 2c60a7f009..d78a9a52d6 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -2747,7 +2747,14 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== -"@svgdotjs/svg.js@^3.2.0": +"@svgdotjs/svg.filter.js@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@svgdotjs/svg.filter.js/-/svg.filter.js-3.0.8.tgz#998cb2481a871fa70d7dbaa891c886b335c562d7" + integrity sha512-YshF2YDaeRA2StyzAs5nUPrev7npQ38oWD0eTRwnsciSL2KrRPMoUw8BzjIXItb3+dccKGTX3IQOd2NFzmHkog== + dependencies: + "@svgdotjs/svg.js" "^3.1.1" + +"@svgdotjs/svg.js@^3.1.1", "@svgdotjs/svg.js@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@svgdotjs/svg.js/-/svg.js-3.2.0.tgz#6baa8cef6778a93818ac18faa2055222e60aa644" integrity sha512-Tr8p+QVP7y+QT1GBlq1Tt57IvedVH8zCPoYxdHLX0Oof3a/PqnC/tXAkVufv1JQJfsDHlH/UrjcDfgxSofqSNA==