From f80ba069ad3c2f80b5b5c9899194bebe26a06886 Mon Sep 17 00:00:00 2001 From: kalytka Date: Mon, 4 Dec 2023 10:31:21 +0200 Subject: [PATCH 001/128] share module --- ui-ngx/src/app/modules/common/modules-map.ts | 24 ++++++++++++++++++- .../home/components/home-components.module.ts | 14 ++++++++--- .../app/modules/home/components/public-api.ts | 2 ++ .../basic/basic-widget-config.module.ts | 11 --------- .../config/widget-config-components.module.ts | 13 ++++++++-- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index d93a84ea51..76294e6330 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -188,7 +188,13 @@ import * as MaterialIconsComponent from '@shared/components/material-icons.compo import * as TbIconComponent from '@shared/components/icon.component'; import * as HintTooltipIconComponent from '@shared/components/hint-tooltip-icon.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 AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.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 EntitiesTableComponent from '@home/components/entity/entities-table.component'; import * as DetailsPanelComponent from '@home/components/details-panel.component'; import * as EntityDetailsPanelComponent from '@home/components/entity/entity-details-panel.component'; @@ -313,6 +319,11 @@ import * as AssetProfileAutocompleteComponent from '@home/components/profile/ass import * as RuleChainSelectComponent from '@shared/components/rule-chain/rule-chain-select.component'; import { IModulesMap } from '@modules/common/modules-map.models'; +import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; +import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; +import { AlarmAssigneePanelComponent } from '@home/components/alarm/alarm-assignee-panel.component'; +import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component'; +import { AlarmFilterConfigComponent } from '@home/components/alarm/alarm-filter-config.component'; declare const System; @@ -495,7 +506,14 @@ class ModulesMap implements IModulesMap { '@shared/components/icon.component': TbIconComponent, '@shared/components/hint-tooltip-icon.component': HintTooltipIconComponent, + '@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/entity/add-entity-dialog.component': AddEntityDialogComponent, + '@home/components/widget/config/datasources.component': DatasourceComponent, + '@home/components/widget/config/basic/common/data-keys-panel.component': DataKeysPanelComponent, '@home/components/entity/entities-table.component': EntitiesTableComponent, '@home/components/details-panel.component': DetailsPanelComponent, '@home/components/entity/entity-details-panel.component': EntityDetailsPanelComponent, @@ -617,7 +635,11 @@ class ModulesMap implements IModulesMap { '@home/components/dashboard-page/dashboard-image-dialog.component': DashboardImageDialogComponent, '@home/components/widget/widget-container.component': WidgetContainerComponent, '@home/components/profile/queue/tenant-profile-queues.component': TenantProfileQueuesComponent, - '@home/components/queue/queue-form.component': QueueFormComponent + '@home/components/queue/queue-form.component': QueueFormComponent, + '@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 }; init() { 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 46e9f15275..e60dadf626 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 @@ -182,10 +182,13 @@ import { ExportWidgetsBundleDialogComponent } from '@home/components/import-export/export-widgets-bundle-dialog.component'; import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component'; +import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; +import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; @NgModule({ declarations: [ + ScrollGridComponent, RouterTabsComponent, EntitiesTableComponent, AddEntityDialogComponent, @@ -326,8 +329,7 @@ import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component RateLimitsComponent, RateLimitsTextComponent, RateLimitsDetailsDialogComponent, - SendNotificationButtonComponent, - ScrollGridComponent + SendNotificationButtonComponent ], imports: [ CommonModule, @@ -339,9 +341,15 @@ import { ScrollGridComponent } from '@home/components/grid/scroll-grid.component SnmpDeviceProfileTransportModule, StatesControllerModule, DeviceCredentialsModule, - DeviceProfileCommonModule + DeviceProfileCommonModule, + WidgetSettingsCommonModule, + WidgetComponentsModule ], exports: [ + WidgetComponentsModule, + SharedHomeComponentsModule, + WidgetSettingsCommonModule, + WidgetConfigComponentsModule, RouterTabsComponent, EntitiesTableComponent, AddEntityDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index aca4d33e39..22748a828c 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -17,3 +17,5 @@ export * from './home-components.module'; export * from './widget/config/widget-config.component.models'; +export * from './widget/lib/table-widget.models'; +export * from './widget/lib/flot-widget.models'; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 5bb6d9ea52..4b41a8ad09 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -22,14 +22,9 @@ import { WidgetConfigComponentsModule } from '@home/components/widget/config/wid import { SimpleCardBasicConfigComponent } from '@home/components/widget/config/basic/cards/simple-card-basic-config.component'; -import { - WidgetActionsPanelComponent -} from '@home/components/widget/config/basic/common/widget-actions-panel.component'; import { EntitiesTableBasicConfigComponent } from '@home/components/widget/config/basic/entity/entities-table-basic-config.component'; -import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; -import { DataKeyRowComponent } from '@home/components/widget/config/basic/common/data-key-row.component'; import { TimeseriesTableBasicConfigComponent } from '@home/components/widget/config/basic/cards/timeseries-table-basic-config.component'; @@ -88,7 +83,6 @@ import { @NgModule({ declarations: [ - WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent, @@ -98,8 +92,6 @@ import { AggregatedValueCardBasicConfigComponent, AggregatedDataKeyRowComponent, AggregatedDataKeysPanelComponent, - DataKeyRowComponent, - DataKeysPanelComponent, AlarmCountBasicConfigComponent, EntityCountBasicConfigComponent, BatteryLevelBasicConfigComponent, @@ -119,7 +111,6 @@ import { WidgetConfigComponentsModule ], exports: [ - WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent, @@ -129,8 +120,6 @@ import { AggregatedValueCardBasicConfigComponent, AggregatedDataKeyRowComponent, AggregatedDataKeysPanelComponent, - DataKeyRowComponent, - DataKeysPanelComponent, AlarmCountBasicConfigComponent, EntityCountBasicConfigComponent, BatteryLevelBasicConfigComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts index ea0d27dcf6..86b1a7457c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts @@ -32,6 +32,9 @@ import { TimewindowConfigPanelComponent } from '@home/components/widget/config/t import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component'; import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; +import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; +import { DataKeyRowComponent } from '@home/components/widget/config/basic/common/data-key-row.component'; +import { WidgetActionsPanelComponent } from '@home/components/widget/config/basic/common/widget-actions-panel.component'; @NgModule({ declarations: @@ -48,7 +51,10 @@ import { TimewindowStylePanelComponent } from '@home/components/widget/config/ti TimewindowStyleComponent, TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - WidgetSettingsComponent + DataKeysPanelComponent, + DataKeyRowComponent, + WidgetSettingsComponent, + WidgetActionsPanelComponent ], imports: [ CommonModule, @@ -61,6 +67,8 @@ import { TimewindowStylePanelComponent } from '@home/components/widget/config/ti AlarmFilterConfigComponent, DataKeysComponent, DataKeyConfigDialogComponent, + DataKeysPanelComponent, + DataKeyRowComponent, DataKeyConfigComponent, DatasourceComponent, DatasourcesComponent, @@ -70,7 +78,8 @@ import { TimewindowStylePanelComponent } from '@home/components/widget/config/ti TimewindowStylePanelComponent, TimewindowConfigPanelComponent, WidgetSettingsComponent, - WidgetSettingsCommonModule + WidgetSettingsCommonModule, + WidgetActionsPanelComponent ] }) export class WidgetConfigComponentsModule { } From 7263a769e9ac67c6a5202a8cea68138fcc93058a Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 8 Dec 2023 08:47:34 +0100 Subject: [PATCH 002/128] Update locale.constant-de_DE.json Wrong commas --- .../assets/locale/locale.constant-de_DE.json | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) 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 7545c37c47..29fc2d10c4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -34,7 +34,7 @@ "unassign": "Zuordnung aufheben", "share": "Teilen", "make-private": "Privat machen", - "make-public": "Öffentlich machen", + "make-public": "Öffentlich machen", "apply": "Anwenden", "apply-changes": "Änderungen übernehmen", "edit-mode": "Bearbeitungsmodus", @@ -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", @@ -139,6 +156,15 @@ "test": "E-Mail Nachricht testen", "activation": "Nachricht Benutzerkontoaktivierung", "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", @@ -270,8 +296,8 @@ "filter-type-edge-type-description": "Rand vom Typ '{{edgeTypes}}'", "filter-type-relations-query": "Beziehungsabfrage", "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Edge-Abfrage", - "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Edge-Abfrage", + "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Objektabfrage", "filter-type-asset-search-query-description": "Objekte vom Typ {{assetTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-device-search-query": "Geräteabfrage", @@ -404,12 +430,12 @@ "add-to-dashboard": "Zum Dashboard hinzufügen", "add-widget-to-dashboard": "Widget zum Dashboard hinzufügen", "selected-attributes": "{ count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } ausgewählt", - "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt" + "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt", "no-attributes-text": "Keine Attribute gefunden", "no-telemetry-text": "Keine Telemetriedaten gefunden", "copy-key": "Schlüssel kopieren", "add-telemetry": "Telemetriedaten hinzufügen", - "copy-value": "Wert kopieren", + "copy-value": "Wert kopieren" }, "audit-log": { "audit": "Audit", @@ -1677,7 +1703,7 @@ "hours": "Stunden", "minutes": "Minuten", "seconds": "Sekunden", - "advanced": "Erweitert" + "advanced": "Erweitert", "predefined": { "yesterday": "Gestern", "day-before-yesterday": "Vorgestern", From ef1a826893e3e84c2d9dc1d8975f5bfb4922dc0a Mon Sep 17 00:00:00 2001 From: fulai Date: Tue, 2 Jan 2024 18:46:26 +0800 Subject: [PATCH 003/128] Update locale.constant-zh_CN.json --- .../assets/locale/locale.constant-zh_CN.json | 148 ++++++++++++++++-- 1 file changed, 134 insertions(+), 14 deletions(-) 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 f0d07fa506..c2394b7656 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": "告警", @@ -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": "设置", @@ -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": "仪表板", @@ -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": "创建时间", @@ -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": "拖放", @@ -3190,8 +3262,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", @@ -3457,7 +3529,8 @@ "version-tag": "版本标签", "version-tag-hint": "自定义标签应与您设备报告的软件包版本相匹配。", "version-max-length": "版本长度应该少于256个字符", - "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置和包类型。" + "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置和包类型。", + "package-file": "包文件" }, "position": { "top": "顶部", @@ -3555,7 +3628,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": { @@ -3638,7 +3712,9 @@ "js-module": "JS 模块", "lwm2m-model": "LWM2M 模型", "pkcs-12": "PKCS #12" - } + }, + "resource-file": "资源文件", + "resource-files": "资源文件" }, "rulechain": { "rulechain": "规则链", @@ -4102,7 +4178,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 {# 秒} }", @@ -4598,7 +4677,24 @@ "deg-per-second": "deg/s", "degrees-brix": "°Bx", "katal": "kat", - "katal-per-cubic-metre": "kat/m^3" + "katal-per-cubic-metre": "kat/m^3", + "milliampere-hour-tags": "mAh", + "ampere-hours-tags": "Ah", + "kiloampere-hours-tags": "kAh", + "nanoampere-tags": "nA", + "picoampere-tags": "pA", + "microampere-tags": "μA", + "milliampere-tags": "mA", + "ampere-tags": "A", + "kiloamperes-tags": "kA", + "microampere-per-square-centimeter-tags": "µA/cm²", + "ampere-per-square-meter-tags": "A/m²", + "ampere-per-meter-tags": "A/m", + "oersted-tags": "Oe", + "bohr-magneton-tags": "μB", + "ampere-meter-squared-tags": "A·m²", + "ampere-meter-tags": "A·m", + "milligram-per-cubic-meter": "mg/m³" }, "user": { "user": "用户", @@ -6600,6 +6696,30 @@ "items-per-page-separator": "of" }, "language": { - "language": "语言" + "language": "语言", + "locales": { + "ca_ES": "Catalan", + "cs_CZ": "Česky", + "da_DK": "Dansk", + "de_DE": "Deutsch", + "el_GR": "Ελληνικά", + "en_US": "English", + "es_ES": "Español", + "fa_IR": "فارسي", + "fr_FR": "Français", + "it_IT": "Italiano", + "ja_JP": "日本語", + "ka_GE": "ქართული", + "ko_KR": "한국어", + "lv_LV": "Latviešu", + "nl_BE": "Koninkrijk België", + "pt_BR": "Português do Brasil", + "ro_RO": "Română", + "sl_SI": "Slovenščina", + "tr_TR": "Türkçe", + "uk_UA": "Українська", + "zh_CN": "简体中文", + "zh_TW": "繁體中文" + } } } From 3c0b4b2029ec0d8ad4524c0acda2aad68d51c083 Mon Sep 17 00:00:00 2001 From: fulai Date: Wed, 3 Jan 2024 11:33:07 +0800 Subject: [PATCH 004/128] Update locale.constant-zh_CN.json --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 c2394b7656..1939b54ec2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -2961,7 +2961,7 @@ "image": { "gallery": "图像库", "search": "搜索图像", - "selected-images": "{count, plural, =1 {已选择 1 个图像} other {已选择 # 个图像}}", + "selected-images": "已选择 { count, plural, =1 {1 个图像} other {# 个图像} }", "created-time": "创建时间", "name": "名称", "name-required": "名称不能为空。", @@ -2976,9 +2976,9 @@ "image-details": "图像详情", "no-images": "未找到图像", "delete-image": "删除图像", - "delete-image-title": "确定要删除图像“{{imageTitle}}”吗?", + "delete-image-title": "确定要删除图像 '{{imageTitle}}' 吗?", "delete-image-text": "请注意,确认后图像将无法恢复。", - "delete-images-title": "确定要删除{count, plural, =1 {1 个图像} other {# 个图像}}吗?", + "delete-images-title": "确定要删除 { count, plural, =1 {1 个图像} other {# 个图像} } 吗?", "delete-images-text": "请注意,确认后所有选定的图像都将被删除,并且所有相关数据将无法恢复。", "list-mode": "列表视图", "grid-mode": "网格视图", From 518db972f6dbbd6fb4a4cab56d39f73fbadcc6fc Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 5 Jan 2024 13:21:16 +0200 Subject: [PATCH 005/128] update widget-component.module --- .../modules/home/components/widget/widget-components.module.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 858856fb4a..eac2f715c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -117,6 +117,7 @@ import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/dough ], exports: [ EntitiesTableWidgetComponent, + SelectEntityDialogComponent, AlarmsTableWidgetComponent, TimeseriesTableWidgetComponent, EntitiesHierarchyWidgetComponent, From d3dad03a9732fc4cd600bccbe3ff2d4d427d83b0 Mon Sep 17 00:00:00 2001 From: kalytka Date: Tue, 9 Jan 2024 15:52:49 +0200 Subject: [PATCH 006/128] Refactoring --- ui-ngx/src/app/modules/common/modules-map.ts | 24 +++++++++---------- .../home/components/home-components.module.ts | 7 +++--- .../basic/basic-widget-config.module.ts | 11 +++++++++ .../config/widget-config-components.module.ts | 13 ++-------- .../widget/widget-components.module.ts | 1 - 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index fbbced965c..b19a588d8b 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -196,9 +196,14 @@ import * as CssUnitSelectComponent from '@home/components/widget/lib/settings/co 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 AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.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'; import * as EntityDetailsPanelComponent from '@home/components/entity/entity-details-panel.component'; @@ -323,11 +328,6 @@ import * as AssetProfileAutocompleteComponent from '@home/components/profile/ass import * as RuleChainSelectComponent from '@shared/components/rule-chain/rule-chain-select.component'; import { IModulesMap } from '@modules/common/modules-map.models'; -import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; -import { AlarmDetailsDialogComponent } from '@home/components/alarm/alarm-details-dialog.component'; -import { AlarmAssigneePanelComponent } from '@home/components/alarm/alarm-assignee-panel.component'; -import { AlarmCommentDialogComponent } from '@home/components/alarm/alarm-comment-dialog.component'; -import { AlarmFilterConfigComponent } from '@home/components/alarm/alarm-filter-config.component'; declare const System; @@ -523,9 +523,13 @@ class ModulesMap implements IModulesMap { '@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/entity/add-entity-dialog.component': AddEntityDialogComponent, '@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, '@home/components/entity/entity-details-panel.component': EntityDetailsPanelComponent, @@ -644,11 +648,7 @@ class ModulesMap implements IModulesMap { '@home/components/dashboard-page/dashboard-image-dialog.component': DashboardImageDialogComponent, '@home/components/widget/widget-container.component': WidgetContainerComponent, '@home/components/profile/queue/tenant-profile-queues.component': TenantProfileQueuesComponent, - '@home/components/queue/queue-form.component': QueueFormComponent, - '@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/queue/queue-form.component': QueueFormComponent }; init() { 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 f9b50082eb..5d1b2d12f0 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 @@ -323,14 +323,14 @@ import { WidgetComponentsModule } from '@home/components/widget/widget-component SharedModule, SharedHomeComponentsModule, WidgetConfigComponentsModule, - BasicWidgetConfigModule, Lwm2mProfileComponentsModule, SnmpDeviceProfileTransportModule, StatesControllerModule, DeviceCredentialsModule, DeviceProfileCommonModule, WidgetSettingsCommonModule, - WidgetComponentsModule + WidgetComponentsModule, + BasicWidgetConfigModule ], exports: [ WidgetComponentsModule, @@ -456,7 +456,8 @@ import { WidgetComponentsModule } from '@home/components/widget/widget-component RateLimitsComponent, RateLimitsTextComponent, RateLimitsDetailsDialogComponent, - SendNotificationButtonComponent + SendNotificationButtonComponent, + BasicWidgetConfigModule ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 1490d60d42..5e95d162cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -22,9 +22,14 @@ import { WidgetConfigComponentsModule } from '@home/components/widget/config/wid import { SimpleCardBasicConfigComponent } from '@home/components/widget/config/basic/cards/simple-card-basic-config.component'; +import { + WidgetActionsPanelComponent +} from '@home/components/widget/config/basic/common/widget-actions-panel.component'; import { EntitiesTableBasicConfigComponent } from '@home/components/widget/config/basic/entity/entities-table-basic-config.component'; +import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; +import { DataKeyRowComponent } from '@home/components/widget/config/basic/common/data-key-row.component'; import { TimeseriesTableBasicConfigComponent } from '@home/components/widget/config/basic/cards/timeseries-table-basic-config.component'; @@ -86,6 +91,7 @@ import { @NgModule({ declarations: [ + WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent, @@ -95,6 +101,8 @@ import { AggregatedValueCardBasicConfigComponent, AggregatedDataKeyRowComponent, AggregatedDataKeysPanelComponent, + DataKeyRowComponent, + DataKeysPanelComponent, AlarmCountBasicConfigComponent, EntityCountBasicConfigComponent, BatteryLevelBasicConfigComponent, @@ -115,6 +123,7 @@ import { WidgetConfigComponentsModule ], exports: [ + WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, TimeseriesTableBasicConfigComponent, @@ -124,6 +133,8 @@ import { AggregatedValueCardBasicConfigComponent, AggregatedDataKeyRowComponent, AggregatedDataKeysPanelComponent, + DataKeyRowComponent, + DataKeysPanelComponent, AlarmCountBasicConfigComponent, EntityCountBasicConfigComponent, BatteryLevelBasicConfigComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts index 86b1a7457c..ea0d27dcf6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts @@ -32,9 +32,6 @@ import { TimewindowConfigPanelComponent } from '@home/components/widget/config/t import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component'; import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; -import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; -import { DataKeyRowComponent } from '@home/components/widget/config/basic/common/data-key-row.component'; -import { WidgetActionsPanelComponent } from '@home/components/widget/config/basic/common/widget-actions-panel.component'; @NgModule({ declarations: @@ -51,10 +48,7 @@ import { WidgetActionsPanelComponent } from '@home/components/widget/config/basi TimewindowStyleComponent, TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - DataKeysPanelComponent, - DataKeyRowComponent, - WidgetSettingsComponent, - WidgetActionsPanelComponent + WidgetSettingsComponent ], imports: [ CommonModule, @@ -67,8 +61,6 @@ import { WidgetActionsPanelComponent } from '@home/components/widget/config/basi AlarmFilterConfigComponent, DataKeysComponent, DataKeyConfigDialogComponent, - DataKeysPanelComponent, - DataKeyRowComponent, DataKeyConfigComponent, DatasourceComponent, DatasourcesComponent, @@ -78,8 +70,7 @@ import { WidgetActionsPanelComponent } from '@home/components/widget/config/basi TimewindowStylePanelComponent, TimewindowConfigPanelComponent, WidgetSettingsComponent, - WidgetSettingsCommonModule, - WidgetActionsPanelComponent + WidgetSettingsCommonModule ] }) export class WidgetConfigComponentsModule { } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 640f1ba970..6679ed0cca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -119,7 +119,6 @@ import { RangeChartWidgetComponent } from '@home/components/widget/lib/chart/ran ], exports: [ EntitiesTableWidgetComponent, - SelectEntityDialogComponent, AlarmsTableWidgetComponent, TimeseriesTableWidgetComponent, EntitiesHierarchyWidgetComponent, From 48446d04b39d4f3bea38d215ad1ae7a016af599a Mon Sep 17 00:00:00 2001 From: fulai Date: Wed, 10 Jan 2024 10:04:50 +0800 Subject: [PATCH 007/128] Update locale.constant-zh_CN.json --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1939b54ec2..b030bba759 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4853,7 +4853,7 @@ "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": "删除部件", From 9f342ba76f2461790685a78209dbd54587a50165 Mon Sep 17 00:00:00 2001 From: fulai Date: Wed, 10 Jan 2024 10:36:25 +0800 Subject: [PATCH 008/128] Update locale.constant-zh_CN.json --- .../assets/locale/locale.constant-zh_CN.json | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) 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 b030bba759..ea560710c8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -1212,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": "函数类型", @@ -1236,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": "当前值;", @@ -1588,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", @@ -2057,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": "取消分配客户", @@ -2341,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}}' 设为公开吗?", @@ -2440,8 +2440,8 @@ "attributes": "属性", "add-attribute": "添加属性", "add-map": "添加映射元素", - "timeseries": "Timeseries", - "add-timeseries": "添加 Timeseries", + "timeseries": "时间序列", + "add-timeseries": "添加时间序列", "field-required": "必填字段", "brokers": "代理服务器组", "add-broker": "添加代理服务器", @@ -2536,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": "函数", @@ -2626,7 +2626,7 @@ "key-type": { "key-type": "键类型", "attribute": "属性", - "timeseries": "Timeseries", + "timeseries": "时间序列", "entity-field": "实体", "constant": "常量", "client-attribute": "客户端属性", @@ -3044,7 +3044,7 @@ "client-attribute": "客户端属性", "shared-attribute": "共享属性", "server-attribute": "服务器属性", - "timeseries": "Timeseries", + "timeseries": "时间序列", "entity-field": "实体字段", "access-token": "访问令牌", "x509": "X.509", @@ -4857,18 +4857,18 @@ "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": "最新值", "rpc": "控件部件", "alarm": "告警部件", "static": "静态部件", - "timeseries-short": "时序", + "timeseries-short": "时间序列", "latest-short": "最新", "rpc-short": "控制", "alarm-short": "告警", @@ -5239,12 +5239,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": "添加新键", @@ -5653,7 +5653,7 @@ "discard-changes": "放弃更改", "entity-attribute-required": "实体属性必填", "entity-coordinate-required": "纬度和经度两个字段都是必需的", - "entity-timeseries-required": "实体 Timeseries 必填", + "entity-timeseries-required": "实体时间序列必填", "get-location": "获取当前位置", "invalid-date": "无效日期", "latitude": "纬度", @@ -5671,19 +5671,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": "部件标题", @@ -5739,7 +5739,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": "共享属性", @@ -5770,7 +5770,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": "双精度", @@ -5934,7 +5934,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": "更新值设置", From 9d6b487a7f41dee987d10207a329151c00324256 Mon Sep 17 00:00:00 2001 From: fulai Date: Wed, 10 Jan 2024 10:38:59 +0800 Subject: [PATCH 009/128] Update locale.constant-zh_CN.json --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ea560710c8..161df5362c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4868,7 +4868,7 @@ "rpc": "控件部件", "alarm": "告警部件", "static": "静态部件", - "timeseries-short": "时间序列", + "timeseries-short": "时序", "latest-short": "最新", "rpc-short": "控制", "alarm-short": "告警", From 84926b92a39f0ed8bc4a54aacf140e49aef4456a Mon Sep 17 00:00:00 2001 From: fulai Date: Wed, 10 Jan 2024 14:27:24 +0800 Subject: [PATCH 010/128] Update locale.constant-zh_CN.json --- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 161df5362c..996fe3140e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -606,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": "资产类型", From f27ce0686efa83a201a070d23d9ab1a02ec3fc4b Mon Sep 17 00:00:00 2001 From: artem Date: Wed, 10 Jan 2024 14:02:54 +0200 Subject: [PATCH 011/128] TbGpsGeofencingActionNode: added presenceMonitoringStrategyOnEachMessage + tests + node details --- .../engine/geo/TbGpsGeofencingActionNode.java | 39 ++- ...bGpsGeofencingActionNodeConfiguration.java | 3 + .../rule/engine/util/GpsGeofencingEvents.java | 23 ++ .../geo/TbGpsGeofencingActionNodeTest.java | 264 ++++++++++++++++++ 4 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/GpsGeofencingEvents.java create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index abdcc5b299..cd88a1eda7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -15,6 +15,8 @@ */ package org.thingsboard.rule.engine.geo; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -28,6 +30,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import java.util.Collections; @@ -39,6 +42,11 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +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; + /** * Created by ashvayka on 19.01.18. */ @@ -46,15 +54,22 @@ import java.util.concurrent.TimeoutException; @RuleNode( type = ComponentType.ACTION, name = "gps geofencing events", + version = 1, configClazz = TbGpsGeofencingActionNodeConfiguration.class, relationTypes = {"Success", "Entered", "Left", "Inside", "Outside"}, nodeDescription = "Produces incoming messages using GPS based geofencing", - nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters", + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters. " + + "

" + + "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 with types Inside or Outside only the first time the geofencing and duration conditions are satisfied; otherwise Success. " + + "If the presence monitoring strategy \"On each message\" is selected, sends messages with types Inside or Outside every time the geofencing condition is satisfied.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGpsGeofencingConfig" ) public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { + private static final String PRESENCE_MONITORING_STRATEGY_ON_EACH_MESSAGE = "presenceMonitoringStrategyOnEachMessage"; private final Map entityStates = new HashMap<>(); private final Gson gson = new Gson(); private final JsonParser parser = new JsonParser(); @@ -83,18 +98,21 @@ 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"); + ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE); told = true; } } + } else { + ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE); + told = true; } if (!told) { ctx.tellSuccess(msg); @@ -127,4 +145,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(PRESENCE_MONITORING_STRATEGY_ON_EACH_MESSAGE)) { + hasChanges = true; + ((ObjectNode) oldConfiguration).put(PRESENCE_MONITORING_STRATEGY_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..ee375083b4 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 presenceMonitoringStrategyOnEachMessage; + @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.setPresenceMonitoringStrategyOnEachMessage(false); return configuration; } } 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/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..dd7536f4ee --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNodeTest.java @@ -0,0 +1,264 @@ +/** + * 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 com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; +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.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.attributes.AttributesService; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +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.server.common.data.msg.TbNodeConnectionType.SUCCESS; + +class TbGpsGeofencingActionNodeTest { + private TbContext ctx; + private TbGpsGeofencingActionNode node; + private AttributesService attributesService; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + attributesService = mock(AttributesService.class); + node = new TbGpsGeofencingActionNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + private static Stream givenPresenceMonitoringStrategyOnEachMessage_whenOnMsg_thenVerifyOutputMsgTypes() { + return Stream.of( + // default config with presenceMonitoringStrategyOnEachMessage false + Arguments.of(false, List.of( + Map.of(ENTERED, 0, INSIDE, 0, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 1), + Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 1), + Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 2) + )), + // default config with presenceMonitoringStrategyOnEachMessage true + Arguments.of(true, List.of( + Map.of(ENTERED, 0, INSIDE, 0, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 2, SUCCESS, 0), + Map.of(ENTERED, 1, INSIDE, 3, SUCCESS, 0) + )) + ); + } + + @ParameterizedTest + @MethodSource + void givenPresenceMonitoringStrategyOnEachMessage_whenOnMsg_thenVerifyOutputMsgTypes( + boolean presenceMonitoringStrategyOnEachMessage, + List> outputMsgTypesCountList + ) throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingActionNodeConfiguration().defaultConfiguration(); + config.setPresenceMonitoringStrategyOnEachMessage(presenceMonitoringStrategyOnEachMessage); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForNewVersionPolygonPerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + + when(ctx.getAttributesService()).thenReturn(attributesService); + when(ctx + .getAttributesService() + .find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getServiceId())) + .thenReturn(Futures.immediateFuture(Optional.empty())); + + // WHEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + node.onMsg(ctx, msg); + + // THEN + verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(0)); + + // WHEN + msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + node.onMsg(ctx, msg); + + // THEN + verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(1)); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(2)); + + // WHEN + config.setMinInsideDuration(0); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctx, msg); + + // THEN + verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(3)); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(4)); + } + + 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; + } + + private void verifyNodeOutputs(ArgumentCaptor newMsgCaptor, Map outputMsgTypesCount) { + verify(this.ctx, times(outputMsgTypesCount.get(ENTERED))).tellNext(newMsgCaptor.capture(), eq(ENTERED)); + verify(this.ctx, times(outputMsgTypesCount.get(INSIDE))).tellNext(newMsgCaptor.capture(), eq(INSIDE)); + verify(this.ctx, times(outputMsgTypesCount.get(SUCCESS))).tellSuccess(newMsgCaptor.capture()); + } + + // 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" + + " \"presenceMonitoringStrategyOnEachMessage\": 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" + + " \"presenceMonitoringStrategyOnEachMessage\": 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" + + " \"presenceMonitoringStrategyOnEachMessage\": 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") + ); + } + + @ParameterizedTest + @MethodSource + void givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig(int givenVersion, String givenConfigStr, boolean hasChanges, String expectedConfigStr) throws TbNodeException { + // GIVEN + JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); + JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); + + // WHEN + TbPair upgradeResult = node.upgrade(givenVersion, givenConfig); + + // THEN + assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); + ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); + assertThat(upgradedConfig).isEqualTo(expectedConfig); + } + +} From 95f30d2be705f8ab0b9e2027fa0000da46a02956 Mon Sep 17 00:00:00 2001 From: fulai Date: Tue, 16 Jan 2024 14:21:40 +0800 Subject: [PATCH 012/128] Update locale.constant-zh_CN.json --- .../assets/locale/locale.constant-zh_CN.json | 836 +++++++++--------- 1 file changed, 418 insertions(+), 418 deletions(-) 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 996fe3140e..10db674ea3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4277,424 +4277,424 @@ "preview": "预览" }, "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", - "milliampere-hour-tags": "mAh", - "ampere-hours-tags": "Ah", - "kiloampere-hours-tags": "kAh", - "nanoampere-tags": "nA", - "picoampere-tags": "pA", - "microampere-tags": "μA", - "milliampere-tags": "mA", - "ampere-tags": "A", - "kiloamperes-tags": "kA", - "microampere-per-square-centimeter-tags": "µA/cm²", - "ampere-per-square-meter-tags": "A/m²", - "ampere-per-meter-tags": "A/m", - "oersted-tags": "Oe", - "bohr-magneton-tags": "μB", - "ampere-meter-squared-tags": "A·m²", - "ampere-meter-tags": "A·m", - "milligram-per-cubic-meter": "mg/m³" + "millimeter": "毫米", + "centimeter": "厘米", + "angstrom": "埃", + "nanometer": "纳米", + "micrometer": "微米", + "meter": "米", + "kilometer": "千米", + "inch": "英寸", + "foot": "英尺", + "yard": "码", + "mile": "英里", + "nautical-mile": "海里", + "astronomical-unit": "天文单位", + "reciprocal-metre": "倒数米", + "meter-per-meter": "米/米", + "steradian": "球面度", + "thou": "千分之一英寸", + "barleycorn": "麦粒", + "hand": "手", + "chain": "链", + "furlong": "英寻", + "league": "里", + "fathom": "英寻", + "cable": "缆", + "link": "连", + "rod": "竿", + "nanogram": "纳克", + "microgram": "微克", + "milligram": "毫克", + "gram": "克", + "kilogram": "千克", + "tonne": "公吨", + "ounce": "盎司", + "pound": "磅", + "stone": "英石", + "hundredweight-count": "百磅", + "short-tons": "短吨", + "dalton": "道尔顿", + "grain": "格令", + "drachm": "打兰", + "quarter": "夸特", + "slug": "斯勒格", + "carat": "克拉", + "cubic-millimeter": "立方毫米", + "cubic-centimeter": "立方厘米", + "cubic-meter": "立方米", + "cubic-kilometer": "立方千米", + "microliter": "微升", + "milliliter": "毫升", + "liter": "升", + "hectoliter": "公石", + "cubic-inch": "立方英寸", + "cubic-foot": "立方英尺", + "cubic-yard": "立方码", + "fluid-ounce": "液盎司", + "pint": "品脱", + "quart": "夸脱", + "gallon": "加仑", + "oil-barrels": "桶", + "cubic-meter-per-kilogram": "立方米/千克", + "gill": "吉尔", + "hogshead": "大桶", + "teaspoon": "茶匙", + "tablespoon": "汤匙", + "cup": "杯", + "celsius": "摄氏度", + "kelvin": "开尔文", + "rankine": "兰氏度", + "fahrenheit": "华氏度", + "percent": "百分比", + "meter-per-second": "米/秒", + "kilometer-per-hour": "千米/时", + "foot-per-second": "英尺/秒", + "mile-per-hour": "英里/时", + "knot": "节", + "millimeters-per-minute": "毫米/分钟", + "kilometer-per-hour-squared": "千米/二次方时", + "foot-per-second-squared": "英尺/二次方秒", + "pascal": "帕斯卡", + "kilopascal": "千帕", + "megapascal": "兆帕", + "gigapascal": "吉帕", + "millibar": "毫巴", + "bar": "巴", + "kilobar": "千巴", + "newton": "牛顿", + "newton-meter": "牛顿米", + "foot-pounds": "英尺磅", + "inch-pounds": "英寸磅", + "newton-per-meter": "牛顿/米", + "atmospheres": "大气压", + "pounds-per-square-inch": "磅/平方英寸", + "torr": "托", + "inches-of-mercury": "英寸汞柱", + "pascal-per-square-meter": "帕斯卡/平方米", + "pound-per-square-inch": "磅/平方英寸", + "newton-per-square-meter": "牛顿/平方米", + "kilogram-force-per-square-meter": "千克力/平方米", + "pascal-per-square-centimeter": "帕斯卡/平方厘米", + "ton-force-per-square-inch": "吨力/平方英寸", + "kilonewton-per-square-meter": "千牛/平方米", + "newton-per-square-millimeter": "牛顿/平方毫米", + "microjoule": "微焦", + "millijoule": "毫焦", + "joule": "焦", + "kilojoule": "千焦", + "megajoule": "兆焦", + "gigajoule": "吉焦", + "watt-hour": "瓦时", + "kilowatt-hour": "千瓦时", + "electron-volts": "电子伏", + "joules-per-coulomb": "焦耳/库仑", + "british-thermal-unit": "英国热单位", + "foot-pound": "英尺磅", + "calorie": "卡路里", + "small-calorie": "小卡", + "kilocalorie": "千卡", + "joule-per-kelvin": "焦耳/开", + "joule-per-kilogram-kelvin": "焦耳/千克·开", + "joule-per-kilogram": "焦耳/千克", + "watt-per-meter-kelvin": "瓦/米·开", + "joule-per-cubic-meter": "焦耳/立方米", + "therm": "热", + "electric-dipole-moment": "电偶极矩", + "magnetic-dipole-moment": "磁偶极矩", + "debye": "戴拜", + "coulomb-per-square-meter-per-volt": "库仑·平方米/伏", + "milliwatt": "毫瓦", + "microwatt": "微瓦", + "watt": "瓦", + "kilowatt": "千瓦", + "megawatt": "兆瓦", + "gigawatt": "吉瓦", + "metric-horsepower": "公制马力", + "milliwatt-per-square-centimeter": "毫瓦/平方厘米", + "watt-per-square-centimeter": "瓦/平方厘米", + "kilowatt-per-square-centimeter": "千瓦/平方厘米", + "milliwatt-per-square-meter": "毫瓦/平方米", + "watt-per-square-meter": "瓦/平方米", + "kilowatt-per-square-meter": "千瓦/平方米", + "watt-per-square-inch": "瓦/平方英寸", + "kilowatt-per-square-inch": "千瓦/平方英寸", + "horsepower": "马力", + "btu-per-hour": "英国热单位/时", + "coulomb": "库仑", + "millicoulomb": "毫库仑", + "microcoulomb": "微库仑", + "picocoulomb": "皮库仑", + "coulomb-per-meter": "库仑/米", + "coulomb-per-cubic-meter": "库仑/立方米", + "coulomb-per-square-meter": "库仑/平方米", + "square-millimeter": "平方毫米", + "square-centimeter": "平方厘米", + "square-meter": "平方米", + "hectare": "公顷", + "square-kilometer": "平方千米", + "square-inch": "平方英寸", + "square-foot": "平方英尺", + "square-yard": "平方码", + "acre": "英亩", + "square-mile": "平方英里", + "are": "公亩", + "barn": "bar", + "circular-inch": "圆英寸", + "milliampere-hour": "毫安时", + "milliampere-hour-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 毫安时, 毫安时, mAh", + "ampere-hours": "安时", + "ampere-hours-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 安培, 安时, Ah", + "kiloampere-hours": "千安时", + "kiloampere-hours-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 千安时, 千安时, kAh", + "nanoampere": "纳安培", + "nanoampere-tags": "电流, 安培, 纳安培, nA", + "picoampere": "皮安培", + "picoampere-tags": "电流, 安培, 皮安培, pA", + "microampere": "微安培", + "microampere-tags": "电流, 微安培, 微安培, μA", + "milliampere": "毫安培", + "milliampere-tags": "电流, 毫安培, 毫安培, mA", + "ampere": "安培", + "ampere-tags": "电流, 电流流动, 电流, 电气流, 安培, 安培, 电流, A", + "kiloamperes": "千安培", + "kiloamperes-tags": "电流, 电流流动, 千安培, kA", + "microampere-per-square-centimeter": "微安培/平方厘米", + "microampere-per-square-centimeter-tags": "电流密度, 每平方厘米的微安培, µA/cm²", + "ampere-per-square-meter": "安培/平方米", + "ampere-per-square-meter-tags": "电流密度, 单位面积电流, 每平方米的安培, A/m²", + "ampere-per-meter": "安培/米", + "ampere-per-meter-tags": "磁场强度, 磁场强度, 每米的安培, A/m", + "oersted": "厄斯特德", + "oersted-tags": "磁场, 厄斯特德, Oe", + "bohr-magneton": "玻尔磁子", + "bohr-magneton-tags": "原子物理, 磁矩, 玻尔磁子, μB", + "ampere-meter-squared": "安培·平方米", + "ampere-meter-squared-tags": "磁矩, 偶极矩, 安培·平方米, A·m²", + "ampere-meter": "安培·米", + "ampere-meter-tags": "磁场, 电流环, 安培·米, A·m", + "nanovolt": "纳伏", + "picovolt": "皮伏", + "millivolts": "毫伏", + "microvolts": "微伏", + "volt": "伏", + "kilovolts": "千伏", + "dbmV": "分贝毫伏", + "dbm": "分贝毫瓦", + "volt-meter": "伏·米", + "kilovolt-meter": "千伏·米", + "megavolt-meter": "兆伏·米", + "microvolt-meter": "微伏·米", + "millivolt-meter": "毫伏·米", + "nanovolt-meter": "纳伏·米", + "ohm": "欧姆", + "microohm": "微欧", + "milliohm": "毫欧", + "kilohm": "千欧", + "megohm": "兆欧", + "gigohm": "吉欧", + "hertz": "赫兹", + "kilohertz": "千赫", + "megahertz": "兆赫", + "gigahertz": "吉赫", + "rpm": "转数/分钟", + "candela-per-square-meter": "坎德拉/平方米", + "candela": "坎德拉", + "lumen": "流明", + "lux": "勒克斯", + "foot-candle": "英尺烛光", + "lumen-per-square-meter": "流明/平方米", + "lux-second": "勒克斯·秒", + "lumen-second": "流明·秒", + "lumens-per-watt": "流明/瓦", + "absorbance": "吸光度", + "mole": "摩尔", + "nanomole": "纳摩尔", + "micromole": "微摩尔", + "millimole": "毫摩尔", + "kilomole": "千摩尔", + "mole-per-cubic-meter": "摩尔/立方米", + "rssi": "接收信号强度指示", + "ppm": "百万分之一", + "ppb": "十亿分之一", + "micrograms-per-cubic-meter": "微克/立方米", + "aqi": "空气质量指数", + "gram-per-cubic-meter": "克/立方米", + "gram-per-kilogram": "比湿", + "millimeters-per-second": "毫米/秒", + "neper": "纳珀", + "bel": "贝尔", + "decibel": "分贝", + "meters-per-second-squared": "平方米/秒", + "becquerel": "贝克勒", + "curie": "居里", + "gray": "戈瑞", + "sievert": "西弗特", + "roentgen": "伦琴", + "cps": "计数/秒", + "rad": "拉德", + "rem": "剂量当量", + "dps": "衰变/秒", + "rutherford": "卢瑟福", + "coulombs-per-kilogram": "库仑/千克", + "becquerels-per-cubic-meter": "贝克勒/立方米", + "curies-per-liter": "居里/升", + "becquerels-per-second": "贝克勒/秒", + "curies-per-second": "居里/秒", + "gy-per-second": "戈瑞/秒", + "watt-per-steradian": "瓦/立体弧度", + "watt-per-square-metre-steradian": "瓦/平方米·弧度", + "ph-level": "酸碱度", + "turbidity": "浊度", + "mg-per-liter": "毫克/升", + "microsiemens-per-centimeter": "微西门子/厘米", + "millisiemens-per-meter": "毫西门子/米", + "siemens-per-meter": "西门子/米", + "kilogram-per-cubic-meter": "千克/立方米", + "gram-per-cubic-centimeter": "克/立方厘米", + "kilogram-per-square-meter": "千克/平方米", + "milligram-per-milliliter": "毫克/毫升", + "milligram-per-cubic-meter": "毫克/立方米", + "pound-per-cubic-foot": "磅/立方英尺", + "ounces-per-cubic-inch": "盎司/立方英寸", + "tons-per-cubic-yard": "吨/立方码", + "particle-density": "颗粒密度", + "kilometers-per-liter": "千米/升", + "miles-per-gallon": "英里/加仑", + "liters-per-100-km": "升/100千米", + "gallons-per-mile": "加仑/英里", + "liters-per-hour": "升/小时", + "gallons-per-hour": "加仑/小时", + "beats-per-minute": "每分钟心跳数", + "millimeters-of-mercury": "毫米汞柱", + "milligrams-per-deciliter": "毫克/分升", + "g-force": "重力加速度", + "kilonewton": "千牛", + "kilogram-force": "千克力", + "pound-force": "磅力", + "kilopound-force": "千磅力", + "dyne": "达因", + "poundal": "磅力秒平方英尺", + "kip": "千磅力", + "gal": "加尔", + "gravity": "重力", + "hectopascal": "百帕", + "atmosphere": "大气压", + "millibars": "毫巴", + "inch-of-mercury": "英寸汞柱", + "richter-scale": "里氏震级", + "second": "秒", + "minute": "分钟", + "hour": "小时", + "day": "天", + "week": "周", + "month": "月", + "year": "年", + "cubic-foot-per-minute": "立方英尺/分钟", + "cubic-meters-per-hour": "立方米/小时", + "cubic-meters-per-second": "立方米/秒", + "liter-per-second": "升/秒", + "liter-per-minute": "升/分钟", + "gallons-per-minute": "加仑/分钟", + "cubic-foot-per-second": "立方英尺/秒", + "milliliters-per-minute": "毫升/分钟", + "bit": "位", + "byte": "字节", + "kilobyte": "千字节", + "megabyte": "兆字节", + "gigabyte": "吉字节", + "terabyte": "太字节", + "petabyte": "拍字节", + "exabyte": "艾字节", + "zettabyte": "泽字节", + "yottabyte": "尧字节", + "bit-per-second": "比特/秒", + "kilobit-per-second": "千比特/秒", + "megabit-per-second": "兆比特/秒", + "gigabit-per-second": "吉比特/秒", + "terabit-per-second": "太比特/秒", + "byte-per-second": "字节/秒", + "kilobyte-per-second": "千字节/秒", + "megabyte-per-second": "兆字节/秒", + "gigabyte-per-second": "吉字节/秒", + "degree": "度", + "radian": "弧度", + "gradian": "百分度", + "mil": "米尔", + "revolution": "圈", + "siemens": "西门子", + "millisiemens": "毫西门子", + "microsiemens": "微西门子", + "kilosiemens": "千西门子", + "megasiemens": "兆西门子", + "gigasiemens": "吉西门子", + "farad": "法拉", + "millifarad": "毫法拉", + "microfarad": "微法拉", + "nanofarad": "纳法拉", + "picofarad": "皮法拉", + "kilofarad": "千法拉", + "megafarad": "兆法拉", + "gigafarad": "吉法拉", + "terfarad": "太法拉", + "farad-per-meter": "法拉/米", + "tesla": "特斯拉", + "gauss": "高斯", + "kilogauss": "千高斯", + "millitesla": "毫特斯拉", + "microtesla": "微特斯拉", + "nanotesla": "纳特斯拉", + "kilotesla": "千特斯拉", + "megatesla": "兆特斯拉", + "millitesla-square-meters": "毫特斯拉平方米", + "gamma": "伽马", + "lambda": "兰姆达", + "square-meter-per-second": "平方米/秒", + "square-centimeter-per-second": "平方厘米/秒", + "stoke": "斯托克", + "centistokes": "厘斯托克", + "square-foot-per-second": "平方英尺/秒", + "square-inch-per-second": "平方英寸/秒", + "pascal-second": "帕斯卡秒", + "centipoise": "厘泊", + "poise": "泊", + "reynolds": "雷诺", + "pound-per-foot-hour": "磅/英尺-小时", + "newton-second-per-square-meter": "牛顿秒/平方米", + "dyne-second-per-square-centimeter": "达因秒/平方厘米", + "kilogram-per-meter-second": "千克/米·秒", + "tesla-square-meters": "特斯拉平方米", + "maxwell": "麦克斯韦", + "tesla-per-meter": "特斯拉/米", + "gauss-per-centimeter": "高斯/厘米", + "weber": "韦伯", + "microweber": "微韦伯", + "milliweber": "毫韦伯", + "gauss-square-centimeter": "高斯平方厘米", + "kilogauss-square-centimeter": "千高斯平方厘米", + "henry": "亨利", + "millihenry": "毫亨利", + "microhenry": "微亨利", + "nanohenry": "纳亨利", + "henry-per-meter": "亨利/米", + "tesla-meter-per-ampere": "特斯拉米/安", + "gauss-per-oersted": "高斯/厘斯特", + "kilogram-per-mole": "千克/摩尔", + "gram-per-mole": "克/摩尔", + "milligram-per-mole": "毫克/摩尔", + "joule-per-mole": "焦耳/摩尔", + "joule-per-mole-kelvin": "焦耳/摩尔·开尔文", + "millivolts-per-meter": "毫伏/米", + "volts-per-meter": "伏/米", + "kilovolts-per-meter": "千伏/米", + "radian-per-second": "弧度/秒", + "radian-per-second-squared": "弧度/二次方秒", + "revolutions-per-minute-per-second": "角加速度", + "revolutions-per-minute-per-second-squared": "角加速度", + "deg-per-second": "度/秒", + "degrees-brix": "白利糖度", + "katal": "卡塔尔", + "katal-per-cubic-metre": "卡塔尔/立方米" }, "user": { "user": "用户", From 9e7f28ff97f9a0a97de8dd0fdc18fffc6f4f06ac Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 18 Jan 2024 16:46:17 +0200 Subject: [PATCH 013/128] UI: Fixed not updated publick image preview when updated image --- .../src/app/shared/components/image/image-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d2783d59a0..510ff97d74 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 }; } }); From ad90b4480c6b0d0fbcaf0e8f1e4cd69f14a4090e Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 19 Jan 2024 11:22:45 +0200 Subject: [PATCH 014/128] Introduce EDGE_CONNECTIVITY and EDGE_FAILURE notification rules to inform about edge's status and errors with connection, bad credentials, etc --- .../install/ThingsboardInstallService.java | 1 + .../service/edge/EdgeContextComponent.java | 4 ++ .../service/edge/rpc/EdgeGrpcService.java | 26 +++++-- .../service/edge/rpc/EdgeGrpcSession.java | 36 +++++++--- .../DefaultSystemDataLoaderService.java | 13 ++++ .../DefaultNotificationCenter.java | 10 ++- .../EdgeConnectivityTriggerProcessor.java | 60 ++++++++++++++++ .../trigger/EdgeFailureTriggerProcessor.java | 54 +++++++++++++++ .../impl/NotificationRuleExportService.java | 20 ++++-- .../impl/NotificationRuleImportService.java | 15 +++- .../controller/AbstractNotifyEntityTest.java | 20 +----- .../service/limits/RateLimitServiceTest.java | 6 +- .../server/common/data/limit/LimitedApi.java | 2 + .../data/notification/NotificationType.java | 5 +- .../EdgeConnectivityNotificationInfo.java | 67 ++++++++++++++++++ .../info/EdgeFailureNotificationInfo.java | 68 +++++++++++++++++++ .../rule/trigger/EdgeConnectivityTrigger.java | 63 +++++++++++++++++ .../rule/trigger/EdgeFailureTrigger.java | 62 +++++++++++++++++ ...ectivityNotificationRuleTriggerConfig.java | 46 +++++++++++++ ...eFailureNotificationRuleTriggerConfig.java | 39 +++++++++++ .../config/NotificationRuleTriggerConfig.java | 2 + .../config/NotificationRuleTriggerType.java | 2 + .../DefaultTenantProfileConfiguration.java | 3 + .../server/dao/edge/BaseEdgeEventService.java | 11 ++- .../DefaultNotificationSettingsService.java | 39 +++++++++-- .../notification/DefaultNotifications.java | 35 +++++++++- .../dao/service/EdgeEventServiceTest.java | 24 +++---- .../rule-notification-dialog.component.html | 47 +++++++++++++ .../rule-notification-dialog.component.ts | 7 ++ .../app/shared/models/limited-api.models.ts | 8 ++- .../app/shared/models/notification.models.ts | 22 +++++- .../help/en_US/notification/edge_connected.md | 57 ++++++++++++++++ .../help/en_US/notification/edge_failure.md | 57 ++++++++++++++++ .../help/en_US/notification/rate_limits.md | 2 +- .../assets/locale/locale.constant-en_US.json | 10 ++- 35 files changed, 868 insertions(+), 75 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java create mode 100644 application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeFailureNotificationInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java create mode 100644 ui-ngx/src/assets/help/en_US/notification/edge_connected.md create mode 100644 ui-ngx/src/assets/help/en_US/notification/edge_failure.md 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..81b17f3402 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -279,6 +279,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..09092d0621 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.EdgeConnectivityTrigger; 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,16 +455,24 @@ 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(EdgeConnectivityTrigger.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) { @@ -470,7 +481,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i 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 f1f473f976..a42a80c318 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.EdgeFailureTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -110,7 +111,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(); @@ -136,7 +137,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; @@ -205,7 +206,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) { } } @@ -313,7 +314,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(); } @@ -465,7 +466,14 @@ 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 errorMsg = String.format("Failed to deliver the batch: {%s}", 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(EdgeFailureTrigger.builder().tenantId(tenantId) + .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); + } + log.warn("[{}][{}] {}, attempt: {}", this.tenantId, this.sessionId, errorMsg, attempt); } log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size()); for (DownlinkMsg downlinkMsg : copy) { @@ -484,8 +492,11 @@ public final class EdgeGrpcSession implements Closeable { if (attempt < MAX_DOWNLINK_ATTEMPTS) { scheduleDownlinkMsgsPackSend(attempt + 1); } else { + String errorMsg = 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(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); stopCurrentSendDownlinkMsgsTask(false); } } else { @@ -779,7 +790,10 @@ public final class EdgeGrpcSession implements Closeable { } } } catch (Exception e) { + String errorMsg = 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(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); return Futures.immediateFailedFuture(e); } return Futures.allAsList(result); @@ -803,15 +817,21 @@ public final class EdgeGrpcSession implements Closeable { .setMaxInboundMessageSize(maxInboundMessageSize) .build(); } + String errorMsg = String.format("Failed to validate the edge! Provided request secret: %s", request.getEdgeSecret()); + ctx.getNotificationRuleProcessor().process(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.BAD_CREDENTIALS) - .setErrorMsg("Failed to validate the edge!") + .setErrorMsg(errorMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } catch (Exception e) { - log.error("[{}] Failed to process edge connection!", request.getEdgeRoutingKey(), e); + String errorMsg = "Failed to process edge connection!"; + ctx.getNotificationRuleProcessor().process(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) + .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); + log.error(errorMsg, e); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.SERVER_UNAVAILABLE) - .setErrorMsg("Failed to process edge connection!") + .setErrorMsg(errorMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } } 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..fcc829886c 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 @@ -697,6 +697,19 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Override 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); + } + }); + } notificationSettingsService.updateDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); } 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 be37d98bb5..5bbde6de81 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 @@ -241,13 +241,11 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple case PLATFORM_USERS: { PlatformUsersNotificationTargetConfig targetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration(); if (targetConfig.getUsersFilter().getType().isForRules() && ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) { - recipients = new PageDataIterable<>(pageLink -> { - return notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink); - }, 500); + recipients = new PageDataIterable<>(pageLink -> + notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink), 500); } else { - recipients = new PageDataIterable<>(pageLink -> { - return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink); - }, 500); + recipients = new PageDataIterable<>(pageLink -> + notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink), 500); } break; } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java new file mode 100644 index 0000000000..7133bd2d76 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.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.EdgeConnectivityNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeConnectivityTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig.EdgeConnectivityEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Service +@RequiredArgsConstructor +public class EdgeConnectivityTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(EdgeConnectivityTrigger trigger, EdgeConnectivityNotificationRuleTriggerConfig triggerConfig) { + EdgeConnectivityEvent event = trigger.isConnected() ? EdgeConnectivityEvent.CONNECTED : EdgeConnectivityEvent.DISCONNECTED; + if (!triggerConfig.getNotifyOn().contains(event)) { + return false; + } + if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { + return triggerConfig.getEdges().contains(trigger.getEdgeId().getId()); + } + return true; + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeConnectivityTrigger trigger) { + return EdgeConnectivityNotificationInfo.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_CONNECTIVITY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java new file mode 100644 index 0000000000..afdfa6eb1f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java @@ -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. + */ +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.EdgeFailureNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeFailureTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Service +@RequiredArgsConstructor +public class EdgeFailureTriggerProcessor implements NotificationRuleTriggerProcessor { + + @Override + public boolean matchesFilter(EdgeFailureTrigger trigger, EdgeFailureNotificationRuleTriggerConfig triggerConfig) { + if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { + return !triggerConfig.getEdges().contains(trigger.getEdgeId().getId()); + } + return true; + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeFailureTrigger trigger) { + return EdgeFailureNotificationInfo.builder() + .tenantId(trigger.getTenantId()) + .edgeId(trigger.getEdgeId()) + .customerId(trigger.getCustomerId()) + .edgeName(trigger.getEdgeName()) + .errorMsg(trigger.getErrorMsg()) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_FAILURE; + } +} 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..61897aaa55 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.EdgeConnectivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; 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_CONNECTIVITY: { + EdgeConnectivityNotificationRuleTriggerConfig triggerConfig = (EdgeConnectivityNotificationRuleTriggerConfig) ruleTriggerConfig; + triggerConfig.setEdges(null); + break; + } + case EDGE_FAILURE: { + EdgeFailureNotificationRuleTriggerConfig triggerConfig = (EdgeFailureNotificationRuleTriggerConfig) ruleTriggerConfig; + triggerConfig.setEdges(null); + break; + } } NotificationRuleRecipientsConfig ruleRecipientsConfig = notificationRule.getRecipientsConfig(); @@ -79,9 +92,8 @@ public class NotificationRuleExportService> escalationTable = new LinkedHashMap<>(recipientsConfig.getEscalationTable()); - escalationTable.replaceAll((delay, targets) -> { - return toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList()); - }); + escalationTable.replaceAll((delay, targets) -> + toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList())); recipientsConfig.setEscalationTable(escalationTable); break; } 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..c0f3991aca 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.EdgeConnectivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; 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/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 9917e837b9..2d37bfce95 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 @@ -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/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..9784209555 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_CONNECTIVITY, + EDGE_FAILURE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java new file mode 100644 index 0000000000..3dfe438ada --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java @@ -0,0 +1,67 @@ +/** + * 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 EdgeConnectivityNotificationInfo 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, + "tenantId", tenantId.toString(), + "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/info/EdgeFailureNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeFailureNotificationInfo.java new file mode 100644 index 0000000000..3db1224705 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeFailureNotificationInfo.java @@ -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. + */ +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.EntityType; +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 EdgeFailureNotificationInfo implements RuleOriginatedNotificationInfo { + + private TenantId tenantId; + private CustomerId customerId; + private EdgeId edgeId; + private String edgeName; + private String errorMsg; + + @Override + public Map getTemplateData() { + return mapOf( + "tenantId", tenantId.toString(), + "edgeId", edgeId.toString(), + "edgeName", edgeName, + "errorMsg", errorMsg + ); + } + + @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/EdgeConnectivityTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.java new file mode 100644 index 0000000000..dbbf1e3c74 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.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.UUID; +import java.util.concurrent.TimeUnit; + +@Data +@Builder +public class EdgeConnectivityTrigger 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(), edgeName, String.valueOf(connected)); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.HOURS.toMillis(3); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.EDGE_CONNECTIVITY; + } + + @Override + public EntityId getOriginatorEntityId() { + return edgeId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java new file mode 100644 index 0000000000..98e1db0527 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.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 EdgeFailureTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final CustomerId customerId; + private final EdgeId edgeId; + private final String edgeName; + private final String errorMsg; + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), edgeName, errorMsg); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.HOURS.toMillis(2); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.EDGE_FAILURE; + } + + @Override + public EntityId getOriginatorEntityId() { + return edgeId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..bf22487614 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java @@ -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. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; +import java.util.UUID; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeConnectivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set edges; // if empty - all edges + @NotEmpty + private Set notifyOn; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_CONNECTIVITY; + } + + public enum EdgeConnectivityEvent { + CONNECTED, DISCONNECTED + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..7dda80b652 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.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.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 +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class EdgeFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set edges; // if empty - all edges + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.EDGE_FAILURE; + } + +} 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..ae2130a3f6 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 = EdgeConnectivityNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTIVITY"), + @Type(value = EdgeFailureNotificationRuleTriggerConfig.class, name = "EDGE_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..93d26140f0 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_CONNECTIVITY, + EDGE_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/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..6a05b98d16 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,12 +19,16 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +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; +import org.thingsboard.server.dao.util.limits.RateLimitService; @Service @Slf4j @@ -32,11 +36,16 @@ 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) { + boolean isEdgeEventTenantRateLimitReached = !rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS, edgeEvent.getTenantId()); + boolean isEdgeEventRateLimitPerEdgeReached = !rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS_PER_EDGE, edgeEvent.getTenantId(), edgeEvent.getEdgeId()); + if (isEdgeEventTenantRateLimitReached || isEdgeEventRateLimitPerEdgeReached) { + 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/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 59787a01a7..3e02f42cba 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 @@ -41,6 +41,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; @@ -53,6 +54,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -187,6 +189,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.edgeConnectivity, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.edgeFailure, tenantAdmins.getId()); } @Override @@ -198,17 +202,40 @@ 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 { + List requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTIVITY, NotificationType.EDGE_FAILURE); + List existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes( + tenantId, requiredNotificationTypes, new PageLink(1)) + .getData() + .stream() + .map(NotificationTemplate::getNotificationType) + .collect(Collectors.toList()); + + NotificationTarget tenantAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.TENANT_ADMINISTRATORS) + .stream() + .findFirst() + .orElseGet(() -> createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(), + tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators")); + + for (NotificationType type : requiredNotificationTypes) { + if (!existingNotificationTypes.contains(type)) { + switch (type) { + case EDGE_CONNECTIVITY: + defaultNotifications.create(tenantId, DefaultNotifications.edgeConnectivity, tenantAdmins.getId()); + break; + case EDGE_FAILURE: + defaultNotifications.create(tenantId, DefaultNotifications.edgeFailure, 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..4e2cc5bbad 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.EdgeConnectivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig.EdgeConnectivityEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; 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,36 @@ 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 edgeConnectivity = DefaultNotification.builder() + .name("Edge connectivity notification") + .type(NotificationType.EDGE_CONNECTIVITY) + .subject("Edge '${edgeName}' is ${eventType}") + .text("Edge '${edgeName}' is now ${eventType}") + .icon("info").color(null) + .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") + .rule(DefaultRule.builder() + .name("Edge connectivity") + .enabled(false) + .triggerConfig(EdgeConnectivityNotificationRuleTriggerConfig.builder() + .edges(null) + .notifyOn(Set.of(EdgeConnectivityEvent.CONNECTED, EdgeConnectivityEvent.DISCONNECTED)) + .build()) + .description("Send notification to tenant admins when Edge changes its connectivity state") + .build()) + .build(); + public static final DefaultNotification edgeFailure = DefaultNotification.builder() + .name("Edge error notification") + .type(NotificationType.EDGE_FAILURE) + .subject("Edge '${edgeName}' received error") + .text("Error message: '${errorMsg}'") + .icon("error").color(null) + .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") + .rule(DefaultRule.builder() + .name("Edge error") + .triggerConfig(EdgeFailureNotificationRuleTriggerConfig.builder().edges(null).build()) + .description("Send notification to tenant admins or to assigned customers to Edge when error occurs") + .build()) + .build(); public static final DefaultNotification jwtSigningKeyIssue = DefaultNotification.builder() .name("JWT Signing Key issue notification") @@ -346,7 +379,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/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/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 75495abec7..ff2bae3a26 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -407,6 +407,53 @@ + + {{ 'notification.edge-trigger-settings' | translate }} +
+
+
+ notification.filter + + + + + alarm.alarm-severity-list + + + {{ alarmSeverityTranslationMap.get(alarmSeverity) | translate }} + + + +
+ + notification.notify-on + + + {{ alarmActionTranslationMap.get(alarmAction) | translate }} + + + + {{ 'notification.notify-on-required' | translate }} + + +
+
+
+
+ + notification.description + + +
+
+
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 9604670757..a0569e5359 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 @@ -98,6 +98,7 @@ export class RuleNotificationDialogComponent extends apiUsageLimitTemplateForm: FormGroup; newPlatformVersionTemplateForm: FormGroup; rateLimitsTemplateForm: FormGroup; + edgeTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -221,6 +222,12 @@ export class RuleNotificationDialogComponent extends } }); + this.edgeTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + }) + } + ); + this.alarmTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ alarmTypes: [null], 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 9943bebf3d..d23e6c5f84 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -474,7 +474,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_CONNECTIVITY = 'EDGE_CONNECTIVITY', + EDGE_FAILURE = 'EDGE_FAILURE' } export const NotificationTypeIcons = new Map([ @@ -585,6 +587,18 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -612,6 +628,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_CONNECTIVITY, 'notification.trigger.edge-connectivity'], + [TriggerType.EDGE_FAILURE, 'notification.trigger.edge-failure'] ]); export interface NotificationUserSettings { diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_connected.md b/ui-ngx/src/assets/help/en_US/notification/edge_connected.md new file mode 100644 index 0000000000..2044ecf708 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/edge_connected.md @@ -0,0 +1,57 @@ +#### Edge 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 ${eventType} +{:copy-code} +``` + +will be transformed to: + +```text +Edge 'DatacenterEdge' is connected +``` + +
+ +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/edge_failure.md b/ui-ngx/src/assets/help/en_US/notification/edge_failure.md new file mode 100644 index 0000000000..d0aaba21a0 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/edge_failure.md @@ -0,0 +1,57 @@ +#### Edge 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; +* `errorMsg` - the string representation of the error, 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}' received error +{:copy-code} +``` + +will be transformed to: + +```text +Edge 'DatacenterEdge' received error +``` + +
+ +The following template: + +```text +Error message: '${errorMsg}' +{:copy-code} +``` + +will be transformed to: + +```text +Error message: 'Failed to process edge connection!' +``` + +
+
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/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9140f0071a..df0bb635cd 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -851,7 +851,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", @@ -3279,6 +3281,7 @@ "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", "edit-notification-recipients-group": "Edit notification recipients group", "edit-notification-template": "Edit notification template", "edit-rule": "Edit rule", @@ -3423,7 +3426,8 @@ "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": "Edge" }, "templates": "Templates", "notification-templates": "Notifications / Templates", @@ -3444,6 +3448,8 @@ "rule-engine-lifecycle-event": "Rule engine lifecycle event", "new-platform-version": "New platform version", "rate-limits": "Exceeded rate limits", + "edge-connectivity": "Edge connectivity", + "edge-failure": "Edge failure", "trigger": "Trigger", "trigger-required": "Trigger is required" }, From bf0cc22223b94fb0b73c55eba03a0237ca16eae4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 19 Jan 2024 17:33:04 +0200 Subject: [PATCH 015/128] Init all actors for isolated rule engines --- .../server/actors/app/AppActor.java | 3 +-- .../server/actors/tenant/TenantActor.java | 20 +++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index 98e0bf7c25..fcd6d7a1f9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -202,8 +202,7 @@ public class AppActor extends ContextAwareActor { return Optional.ofNullable(ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), () -> DefaultActorService.TENANT_DISPATCHER_NAME, () -> new TenantActor.ActorCreator(systemContext, tenantId), - () -> systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE) || - systemContext.getPartitionService().isManagedByCurrentService(tenantId))); + () -> true)); } private void onToEdgeSessionMsg(EdgeSessionMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index 01b01a7d4a..04c0f1c9e9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -90,18 +90,16 @@ public class TenantActor extends RuleChainManagerActor { isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); if (isRuleEngine) { - if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) { - try { - if (getApiUsageState().isReExecEnabled()) { - log.debug("[{}] Going to init rule chains", tenantId); - initRuleChains(); - } else { - log.info("[{}] Skip init of the rule chains due to API limits", tenantId); - } - } catch (Exception e) { - log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); - cantFindTenant = true; + try { + if (getApiUsageState().isReExecEnabled()) { + log.debug("[{}] Going to init rule chains", tenantId); + initRuleChains(); + } else { + log.info("[{}] Skip init of the rule chains due to API limits", tenantId); } + } catch (Exception e) { + log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); + cantFindTenant = true; } } log.debug("[{}] Tenant actor started.", tenantId); From b87699dc8d463b0e9a41fd84cddb88420f2080ac Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 23 Jan 2024 11:20:36 +0200 Subject: [PATCH 016/128] UI: Fixed progress bar --- .../vc/repository-settings.component.html | 2 +- .../vc/repository-settings.component.ts | 3 +++ .../vc/version-control.component.html | 6 ++++-- ui-ngx/src/app/modules/home/home.component.ts | 20 ++++++++++++++++--- .../home/pages/admin/admin-routing.module.ts | 3 +++ .../notification-routing.module.ts | 1 + .../widget/widget-library-routing.module.ts | 1 + 7 files changed, 30 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html index 10fae28b6d..e0d33fdfd5 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html @@ -26,7 +26,7 @@
- +
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..b2af171353 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 @@ -48,6 +48,9 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit @Input() popoverComponent: TbPopoverComponent; + @Input() + 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..566c1b149f 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"> , @Inject(WINDOW) private window: Window, private activeComponentService: ActiveComponentService, - public breakpointObserver: BreakpointObserver, - private fb: FormBuilder) { + private fb: FormBuilder, + private route: ActivatedRoute, + public breakpointObserver: BreakpointObserver) { super(store); } @@ -144,9 +147,20 @@ 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; + let showLoadingBar: boolean; + if (isDefined(this.route.children[0]?.snapshot?.data?.showLoadingBar)) { + showLoadingBar = this.route.children[0]?.snapshot?.data?.showLoadingBar; + } else if (isDefined(this.route.children[0]?.children[0]?.snapshot?.data?.showLoadingBar)) { + showLoadingBar = this.route.children[0]?.children[0]?.snapshot?.data?.showLoadingBar; + } + if (activeComponent && activeComponent instanceof RouterTabsComponent) { + this.hideLoadingBar = isDefinedAndNotNull(showLoadingBar) ? !showLoadingBar : true; + } else if (isDefinedAndNotNull(showLoadingBar)) { + this.hideLoadingBar = !showLoadingBar; + } if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { this.searchEnabled = true; this.searchableComponent = this.activeComponent; 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..c91448c7d7 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 @@ -323,6 +323,7 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], + showLoadingBar: false, title: 'admin.general', breadcrumb: { label: 'admin.general', @@ -336,6 +337,7 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], + showLoadingBar: false, title: 'admin.2fa.2fa', breadcrumb: { label: 'admin.2fa.2fa', @@ -349,6 +351,7 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], + showLoadingBar: false, title: 'admin.oauth2.oauth2', breadcrumb: { label: 'admin.oauth2.oauth2', diff --git a/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts b/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts index 41f49ee708..995bda9234 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/notification-routing.module.ts @@ -32,6 +32,7 @@ const routes: Routes = [ component: RouterTabsComponent, data: { auth: [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER, Authority.SYS_ADMIN], + showLoadingBar: true, breadcrumb: { label: 'notification.notification-center', icon: 'mdi:message-badge' diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts index c808bae163..160a0e18da 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts @@ -234,6 +234,7 @@ export const widgetsLibraryRoutes: Routes = [ component: RouterTabsComponent, data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + showLoadingBar: true, breadcrumb: { label: 'widget.widget-library', icon: 'now_widgets' From d98a8cd2fa320b676a02b99190c277e21e88bc5d Mon Sep 17 00:00:00 2001 From: deaflynx Date: Tue, 23 Jan 2024 13:47:20 +0200 Subject: [PATCH 017/128] Update notification and rate limit configurations for edge connectivity/failure events --- .../EdgeConnectivityTriggerProcessor.java | 2 +- ...ectivityNotificationRuleTriggerConfig.java | 1 - ...enant-profile-configuration.component.html | 18 +++-- ...-tenant-profile-configuration.component.ts | 4 +- .../tenant/rate-limits/rate-limits.models.ts | 8 ++- .../rule-notification-dialog.component.html | 66 +++++++++++-------- .../rule-notification-dialog.component.ts | 24 +++++-- ui-ngx/src/app/shared/models/edge.models.ts | 12 ++++ ...edge_connected.md => edge_connectivity.md} | 0 .../assets/locale/locale.constant-en_US.json | 12 +++- 10 files changed, 105 insertions(+), 42 deletions(-) rename ui-ngx/src/assets/help/en_US/notification/{edge_connected.md => edge_connectivity.md} (100%) diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java index 7133bd2d76..b5418f6abb 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java @@ -32,7 +32,7 @@ public class EdgeConnectivityTriggerProcessor implements NotificationRuleTrigger @Override public boolean matchesFilter(EdgeConnectivityTrigger trigger, EdgeConnectivityNotificationRuleTriggerConfig triggerConfig) { EdgeConnectivityEvent event = trigger.isConnected() ? EdgeConnectivityEvent.CONNECTED : EdgeConnectivityEvent.DISCONNECTED; - if (!triggerConfig.getNotifyOn().contains(event)) { + if (CollectionUtils.isEmpty(triggerConfig.getNotifyOn()) || !triggerConfig.getNotifyOn().contains(event)) { return false; } if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java index bf22487614..e79f9b0968 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java @@ -31,7 +31,6 @@ import java.util.UUID; public class EdgeConnectivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { private Set edges; // if empty - all edges - @NotEmpty private Set notifyOn; @Override 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 - + + {{ 'notification.edge-trigger-settings' | translate }} -
+
notification.filter - - - + + - alarm.alarm-severity-list - - - {{ alarmSeverityTranslationMap.get(alarmSeverity) | translate }} + notification.notify-on + + + {{ edgeConnectivityEventTranslationMap.get(edgeEvent) | translate }}
- - notification.notify-on - - - {{ alarmActionTranslationMap.get(alarmAction) | translate }} - - - - {{ 'notification.notify-on-required' | translate }} - +
+
+
+
+ + notification.description +
+
+ + + {{ 'notification.edge-trigger-settings' | translate }} +
+
+
+ notification.filter + + +
+
+
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 a0569e5359..099897af7c 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 { EdgeConnectivityEvent, EdgeConnectivityEventTranslationMap } from '@shared/models/edge.models'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -98,7 +99,8 @@ export class RuleNotificationDialogComponent extends apiUsageLimitTemplateForm: FormGroup; newPlatformVersionTemplateForm: FormGroup; rateLimitsTemplateForm: FormGroup; - edgeTemplateForm: FormGroup; + edgeFailureTemplateForm: FormGroup; + edgeConnectivityTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -133,6 +135,9 @@ export class RuleNotificationDialogComponent extends apiFeatures: ApiFeature[] = Object.values(ApiFeature); apiFeatureTranslationMap = ApiFeatureTranslationMap; + edgeConnectivityEvents: EdgeConnectivityEvent[] = Object.values(EdgeConnectivityEvent); + edgeConnectivityEventTranslationMap = EdgeConnectivityEventTranslationMap; + limitedApis: StringItemsOption[]; entityType = EntityType; @@ -222,11 +227,18 @@ export class RuleNotificationDialogComponent extends } }); - this.edgeTemplateForm = this.fb.group({ + this.edgeConnectivityTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ + edges: [null], + notifyOn: [null] }) - } - ); + }); + + this.edgeFailureTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + edges: [null] + }) + }); this.alarmTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ @@ -335,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_FAILURE, this.edgeFailureTemplateForm], + [TriggerType.EDGE_CONNECTIVITY, this.edgeConnectivityTemplateForm] ]); if (data.isAdd || data.isCopy) { diff --git a/ui-ngx/src/app/shared/models/edge.models.ts b/ui-ngx/src/app/shared/models/edge.models.ts index 7187dc8eaa..4da5f089a0 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 EdgeConnectivityEvent { + CONNECTED= 'CONNECTED', + DISCONNECTED = 'DISCONNECTED' +} + +export const EdgeConnectivityEventTranslationMap = new Map( + [ + [EdgeConnectivityEvent.CONNECTED, 'edge-event.connected'], + [EdgeConnectivityEvent.DISCONNECTED, 'edge-event.disconnected'] + ] +); diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_connected.md b/ui-ngx/src/assets/help/en_US/notification/edge_connectivity.md similarity index 100% rename from ui-ngx/src/assets/help/en_US/notification/edge_connected.md rename to ui-ngx/src/assets/help/en_US/notification/edge_connectivity.md 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 df0bb635cd..63b791a6ef 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2073,7 +2073,9 @@ "action-type-assigned-to-edge": "Assigned to Edge", "action-type-unassigned-from-edge": "Unassigned from Edge", "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request" + "action-type-entity-merge-request": "Entity Merge Request", + "connected": "Connected", + "disconnected": "Disconnected" }, "error": { "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", @@ -3282,6 +3284,7 @@ "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", @@ -3427,7 +3430,8 @@ "rule-node": "Rule node", "new-platform-version": "New platform version", "rate-limits": "Exceeded rate limits", - "edge": "Edge" + "edge-failure": "Edge error", + "edge-connectivity": "Edge connectivity" }, "templates": "Templates", "notification-templates": "Notifications / Templates", @@ -4173,6 +4177,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", From 2d6dba7772207e46dbcc6291eb468c430467aece Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 23 Jan 2024 15:27:53 +0200 Subject: [PATCH 018/128] UI: Refactoring --- .../vc/repository-settings.component.html | 2 +- .../vc/repository-settings.component.ts | 2 ++ .../vc/version-control.component.html | 2 +- ui-ngx/src/app/modules/home/home.component.ts | 10 +++++----- .../home/pages/admin/admin-routing.module.ts | 3 --- .../pages/admin/oauth2-settings.component.ts | 20 +++++++++++++++---- .../admin/security-settings.component.ts | 13 ++++++------ .../two-factor-auth-settings.component.ts | 5 ++++- .../modules/home/pages/home-pages.models.ts | 4 ++++ 9 files changed, 40 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html index e0d33fdfd5..7ba277364d 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html @@ -28,7 +28,7 @@ -
+
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 b2af171353..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', @@ -49,6 +50,7 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit popoverComponent: TbPopoverComponent; @Input() + @coerceBoolean() hideLoadingBar = false; repositorySettingsForm: UntypedFormGroup; 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 566c1b149f..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 @@ -17,7 +17,7 @@ --> diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index 3042061bb5..f88cdc9dec 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'; @@ -151,10 +151,10 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni this.textSearch.reset('', {emitEvent: false}); this.activeComponent = activeComponent; let showLoadingBar: boolean; - if (isDefined(this.route.children[0]?.snapshot?.data?.showLoadingBar)) { - showLoadingBar = this.route.children[0]?.snapshot?.data?.showLoadingBar; - } else if (isDefined(this.route.children[0]?.children[0]?.snapshot?.data?.showLoadingBar)) { - showLoadingBar = this.route.children[0]?.children[0]?.snapshot?.data?.showLoadingBar; + if (isDefined(this.activeComponent.activatedRoute?.data?.value?.showLoadingBar)) { + showLoadingBar = this.activeComponent.activatedRoute?.data?.value?.showLoadingBar; + } else if (isDefined(this.activeComponent?.showLoadingBar)) { + showLoadingBar = this.activeComponent.showLoadingBar; } if (activeComponent && activeComponent instanceof RouterTabsComponent) { this.hideLoadingBar = isDefinedAndNotNull(showLoadingBar) ? !showLoadingBar : true; 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 c91448c7d7..74c5012791 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 @@ -323,7 +323,6 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], - showLoadingBar: false, title: 'admin.general', breadcrumb: { label: 'admin.general', @@ -337,7 +336,6 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], - showLoadingBar: false, title: 'admin.2fa.2fa', breadcrumb: { label: 'admin.2fa.2fa', @@ -351,7 +349,6 @@ const routes: Routes = [ canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN], - showLoadingBar: false, title: 'admin.oauth2.oauth2', breadcrumb: { label: 'admin.oauth2.oauth2', 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..798b07c461 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'; @@ -45,13 +54,14 @@ import { TranslateService } from '@ngx-translate/core'; import { isDefined, isDefinedAndNotNull, randomAlphanumeric } from '@core/utils'; import { OAuth2Service } from '@core/http/oauth2.service'; import { ActivatedRoute } from '@angular/router'; +import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-oauth2-settings', templateUrl: './oauth2-settings.component.html', styleUrls: ['./oauth2-settings.component.scss', './settings-card.scss'] }) -export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { +export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, HasShowLoading, OnDestroy { constructor(protected store: Store, private route: ActivatedRoute, @@ -105,6 +115,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha templateProvider = ['Custom']; + showLoadingBar = 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..f9449eba33 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,21 +36,21 @@ 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'; +import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-security-settings', templateUrl: './security-settings.component.html', styleUrls: ['./security-settings.component.scss', './settings-card.scss'] }) -export class SecuritySettingsComponent extends PageComponent implements HasConfirmForm { +export class SecuritySettingsComponent extends PageComponent implements HasConfirmForm, HasShowLoading { securitySettingsFormGroup: UntypedFormGroup; jwtSecuritySettingsFormGroup: UntypedFormGroup; + showLoadingBar = 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..208d133e3e 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 @@ -32,13 +32,14 @@ import { isNotEmptyStr } from '@core/utils'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { MatExpansionPanel } from '@angular/material/expansion'; +import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-2fa-settings', templateUrl: './two-factor-auth-settings.component.html', styleUrls: [ './settings-card.scss', './two-factor-auth-settings.component.scss'] }) -export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { +export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, HasShowLoading, OnDestroy { private readonly destroy$ = new Subject(); private readonly posIntValidation = [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]; @@ -47,6 +48,8 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI twoFactorAuthProviderType = TwoFactorAuthProviderType; twoFactorAuthProvidersData = twoFactorAuthProvidersData; + showLoadingBar = 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..456bbb6999 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 @@ -31,3 +31,7 @@ export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction Date: Tue, 23 Jan 2024 15:41:02 +0200 Subject: [PATCH 019/128] UI: Remove route --- ui-ngx/src/app/modules/home/home.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index f88cdc9dec..a314803e9c 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -73,7 +73,6 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni @Inject(WINDOW) private window: Window, private activeComponentService: ActiveComponentService, private fb: FormBuilder, - private route: ActivatedRoute, public breakpointObserver: BreakpointObserver) { super(store); } From 16fa1780c0750af56508d2fbf01aac54e18e07d3 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Wed, 24 Jan 2024 13:25:02 +0200 Subject: [PATCH 020/128] UI: liquid level widget improvements --- ...uid-level-card-basic-config.component.html | 24 ++++++- ...iquid-level-card-basic-config.component.ts | 6 +- .../liquid-level-widget.component.ts | 63 ++++++++++++++----- .../indicator/liquid-level-widget.models.ts | 31 +++++++-- ...-level-card-widget-settings.component.html | 24 ++++++- ...id-level-card-widget-settings.component.ts | 4 +- .../assets/locale/locale.constant-en_US.json | 1 + 7 files changed, 127 insertions(+), 26 deletions(-) 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..4940df18ba 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 @@ -184,10 +184,30 @@ formControlName="volumeAttributeName"> - +
+
+
widgets.liquid-level-card.total-volume-units
+
+ + + + {{ DataSourceTypeTranslations.get(type) | 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/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/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..d549198273 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 @@ -141,10 +141,30 @@ formControlName="volumeAttributeName"> - +
+
+
widgets.liquid-level-card.total-volume-units
+
+ + + + {{ DataSourceTypeTranslations.get(type) | 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/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 9140f0071a..6ab58e8e50 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6241,6 +6241,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", From d4d019bedac3d9a0ffe7f98c2423c80814066b7c Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 24 Jan 2024 15:21:43 +0200 Subject: [PATCH 021/128] Renaming notifications for edge. Minor refactoring --- .../service/edge/rpc/EdgeGrpcService.java | 4 +- .../service/edge/rpc/EdgeGrpcSession.java | 52 ++++++++++--------- ...CommunicationFailureTriggerProcessor.java} | 26 ++++++---- ...va => EdgeConnectionTriggerProcessor.java} | 18 +++---- .../impl/NotificationRuleExportService.java | 12 ++--- .../impl/NotificationRuleImportService.java | 12 ++--- .../data/notification/NotificationType.java | 4 +- ...CommunicationFailureNotificationInfo.java} | 7 ++- ...va => EdgeConnectionNotificationInfo.java} | 2 +- ...a => EdgeCommunicationFailureTrigger.java} | 11 ++-- ...rigger.java => EdgeConnectionTrigger.java} | 7 ++- ...FailureNotificationRuleTriggerConfig.java} | 4 +- ...nectionNotificationRuleTriggerConfig.java} | 5 +- .../config/NotificationRuleTriggerConfig.java | 4 +- .../config/NotificationRuleTriggerType.java | 4 +- .../common/data/util/TemplateUtils.java | 3 +- .../DefaultNotificationSettingsService.java | 14 ++--- .../notification/DefaultNotifications.java | 39 +++++++------- .../rule-notification-dialog.component.html | 18 +++---- .../rule-notification-dialog.component.ts | 18 +++---- ui-ngx/src/app/shared/models/edge.models.ts | 8 +-- .../app/shared/models/notification.models.ts | 24 ++++----- ...ilure.md => edge_communication_failure.md} | 10 ++-- ...dge_connectivity.md => edge_connection.md} | 17 +----- .../assets/locale/locale.constant-en_US.json | 16 +++--- 25 files changed, 168 insertions(+), 171 deletions(-) rename application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/{EdgeFailureTriggerProcessor.java => EdgeCommunicationFailureTriggerProcessor.java} (65%) rename application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/{EdgeConnectivityTriggerProcessor.java => EdgeConnectionTriggerProcessor.java} (77%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/info/{EdgeFailureNotificationInfo.java => EdgeCommunicationFailureNotificationInfo.java} (89%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/info/{EdgeConnectivityNotificationInfo.java => EdgeConnectionNotificationInfo.java} (95%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/{EdgeFailureTrigger.java => EdgeCommunicationFailureTrigger.java} (84%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/{EdgeConnectivityTrigger.java => EdgeConnectionTrigger.java} (89%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/{EdgeFailureNotificationRuleTriggerConfig.java => EdgeCommunicationFailureNotificationRuleTriggerConfig.java} (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/{EdgeConnectivityNotificationRuleTriggerConfig.java => EdgeConnectionNotificationRuleTriggerConfig.java} (84%) rename ui-ngx/src/assets/help/en_US/notification/{edge_failure.md => edge_communication_failure.md} (78%) rename ui-ngx/src/assets/help/en_US/notification/{edge_connectivity.md => edge_connection.md} (84%) 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 09092d0621..c23fd98d8e 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,7 +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.EdgeConnectivityTrigger; +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; @@ -467,7 +467,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, false); edgeState.put(DefaultDeviceStateService.LAST_DISCONNECT_TIME, ts); } - ctx.getNotificationRuleProcessor().process(EdgeConnectivityTrigger.builder() + ctx.getNotificationRuleProcessor().process(EdgeConnectionTrigger.builder() .tenantId(tenantId) .customerId(edge.getCustomerId()) .edgeId(edgeId) 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 a42a80c318..44bdfcdb1c 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,7 +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.EdgeFailureTrigger; +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; @@ -466,22 +466,24 @@ public final class EdgeGrpcSession implements Closeable { if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) { List copy = new ArrayList<>(sessionState.getPendingMsgsMap().values()); if (attempt > 1) { - String errorMsg = String.format("Failed to deliver the batch: {%s}", copy); + 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(EdgeFailureTrigger.builder().tenantId(tenantId) - .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); + 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, errorMsg, attempt); + 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 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(message).build()); sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId()); } else { sendDownlinkMsg(ResponseMsg.newBuilder() @@ -492,11 +494,12 @@ public final class EdgeGrpcSession implements Closeable { if (attempt < MAX_DOWNLINK_ATTEMPTS) { scheduleDownlinkMsgsPackSend(attempt + 1); } else { - String errorMsg = String.format("Failed to deliver messages: %s", copy); + 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(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) - .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); + 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 { @@ -790,10 +793,10 @@ public final class EdgeGrpcSession implements Closeable { } } } catch (Exception e) { - String errorMsg = String.format("Can't process uplink msg [%s] from edge", uplinkMsg); + 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(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) - .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); + 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); @@ -817,21 +820,22 @@ public final class EdgeGrpcSession implements Closeable { .setMaxInboundMessageSize(maxInboundMessageSize) .build(); } - String errorMsg = String.format("Failed to validate the edge! Provided request secret: %s", request.getEdgeSecret()); - ctx.getNotificationRuleProcessor().process(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) - .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).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(errorMsg) + .setErrorMsg(failureMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } catch (Exception e) { - String errorMsg = "Failed to process edge connection!"; - ctx.getNotificationRuleProcessor().process(EdgeFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) - .customerId(edge.getCustomerId()).edgeName(edge.getName()).errorMsg(errorMsg).build()); - log.error(errorMsg, 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(errorMsg) + .setErrorMsg(failureMsg) .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java similarity index 65% rename from application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java rename to application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java index afdfa6eb1f..dc54024eec 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeFailureTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeCommunicationFailureTriggerProcessor.java @@ -18,18 +18,18 @@ 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.EdgeFailureNotificationInfo; +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.EdgeFailureTrigger; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; +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 EdgeFailureTriggerProcessor implements NotificationRuleTriggerProcessor { +public class EdgeCommunicationFailureTriggerProcessor implements NotificationRuleTriggerProcessor { @Override - public boolean matchesFilter(EdgeFailureTrigger trigger, EdgeFailureNotificationRuleTriggerConfig triggerConfig) { + public boolean matchesFilter(EdgeCommunicationFailureTrigger trigger, EdgeCommunicationFailureNotificationRuleTriggerConfig triggerConfig) { if (CollectionUtils.isNotEmpty(triggerConfig.getEdges())) { return !triggerConfig.getEdges().contains(trigger.getEdgeId().getId()); } @@ -37,18 +37,26 @@ public class EdgeFailureTriggerProcessor implements NotificationRuleTriggerProce } @Override - public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeFailureTrigger trigger) { - return EdgeFailureNotificationInfo.builder() + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeCommunicationFailureTrigger trigger) { + return EdgeCommunicationFailureNotificationInfo.builder() .tenantId(trigger.getTenantId()) .edgeId(trigger.getEdgeId()) .customerId(trigger.getCustomerId()) .edgeName(trigger.getEdgeName()) - .errorMsg(trigger.getErrorMsg()) + .failureMsg(truncateFailureMsg(trigger.getFailureMsg())) .build(); } @Override public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.EDGE_FAILURE; + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; + } + + private String truncateFailureMsg(String input) { + int maxLength = 800; + 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/EdgeConnectivityTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java similarity index 77% rename from application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java rename to application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java index b5418f6abb..ed1ee74622 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectivityTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EdgeConnectionTriggerProcessor.java @@ -18,19 +18,19 @@ 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.EdgeConnectivityNotificationInfo; +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.EdgeConnectivityTrigger; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig.EdgeConnectivityEvent; +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 EdgeConnectivityTriggerProcessor implements NotificationRuleTriggerProcessor { +public class EdgeConnectionTriggerProcessor implements NotificationRuleTriggerProcessor { @Override - public boolean matchesFilter(EdgeConnectivityTrigger trigger, EdgeConnectivityNotificationRuleTriggerConfig triggerConfig) { + 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; @@ -42,8 +42,8 @@ public class EdgeConnectivityTriggerProcessor implements NotificationRuleTrigger } @Override - public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeConnectivityTrigger trigger) { - return EdgeConnectivityNotificationInfo.builder() + public RuleOriginatedNotificationInfo constructNotificationInfo(EdgeConnectionTrigger trigger) { + return EdgeConnectionNotificationInfo.builder() .eventType(trigger.isConnected() ? "connected" : "disconnected") .tenantId(trigger.getTenantId()) .customerId(trigger.getCustomerId()) @@ -54,7 +54,7 @@ public class EdgeConnectivityTriggerProcessor implements NotificationRuleTrigger @Override public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.EDGE_CONNECTIVITY; + return NotificationRuleTriggerType.EDGE_CONNECTION; } } 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 61897aaa55..40d7d7c2b5 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,8 +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.EdgeConnectivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; +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.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.sync.ie.EntityExportData; @@ -75,13 +75,13 @@ public class NotificationRuleExportService getTemplateData() { @@ -47,7 +46,7 @@ public class EdgeFailureNotificationInfo implements RuleOriginatedNotificationIn "tenantId", tenantId.toString(), "edgeId", edgeId.toString(), "edgeName", edgeName, - "errorMsg", errorMsg + "failureMsg", failureMsg ); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java similarity index 95% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java index 3dfe438ada..e7b6494fdb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectivityNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/EdgeConnectionNotificationInfo.java @@ -32,7 +32,7 @@ import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; @NoArgsConstructor @AllArgsConstructor @Builder -public class EdgeConnectivityNotificationInfo implements RuleOriginatedNotificationInfo { +public class EdgeConnectionNotificationInfo implements RuleOriginatedNotificationInfo { private String eventType; private TenantId tenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java similarity index 84% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java index 98e1db0527..6400a5c67a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeFailureTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeCommunicationFailureTrigger.java @@ -27,13 +27,14 @@ import java.util.concurrent.TimeUnit; @Data @Builder -public class EdgeFailureTrigger implements NotificationRuleTrigger { +public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger { private final TenantId tenantId; private final CustomerId customerId; private final EdgeId edgeId; private final String edgeName; - private final String errorMsg; + private final String failureMsg; + private final String error; @Override public boolean deduplicate() { @@ -42,17 +43,17 @@ public class EdgeFailureTrigger implements NotificationRuleTrigger { @Override public String getDeduplicationKey() { - return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), edgeName, errorMsg); + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), edgeId.toString(), error); } @Override public long getDefaultDeduplicationDuration() { - return TimeUnit.HOURS.toMillis(2); + return TimeUnit.MINUTES.toMillis(30); } @Override public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.EDGE_FAILURE; + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java index dbbf1e3c74..fe5f6fa6b1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectivityTrigger.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EdgeConnectionTrigger.java @@ -23,12 +23,11 @@ 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.UUID; import java.util.concurrent.TimeUnit; @Data @Builder -public class EdgeConnectivityTrigger implements NotificationRuleTrigger { +public class EdgeConnectionTrigger implements NotificationRuleTrigger { private final TenantId tenantId; private final CustomerId customerId; @@ -48,12 +47,12 @@ public class EdgeConnectivityTrigger implements NotificationRuleTrigger { @Override public long getDefaultDeduplicationDuration() { - return TimeUnit.HOURS.toMillis(3); + return TimeUnit.MINUTES.toMillis(30); } @Override public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.EDGE_CONNECTIVITY; + return NotificationRuleTriggerType.EDGE_CONNECTION; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java index 7dda80b652..595b0fa7d5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeFailureNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeCommunicationFailureNotificationRuleTriggerConfig.java @@ -27,13 +27,13 @@ import java.util.UUID; @NoArgsConstructor @AllArgsConstructor @Builder -public class EdgeFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { +public class EdgeCommunicationFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { private Set edges; // if empty - all edges @Override public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.EDGE_FAILURE; + return NotificationRuleTriggerType.EDGE_COMMUNICATION_FAILURE; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java similarity index 84% rename from common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java rename to common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java index e79f9b0968..8c0905cc59 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectivityNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EdgeConnectionNotificationRuleTriggerConfig.java @@ -20,7 +20,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotEmpty; import java.util.Set; import java.util.UUID; @@ -28,14 +27,14 @@ import java.util.UUID; @NoArgsConstructor @AllArgsConstructor @Builder -public class EdgeConnectivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { +public class EdgeConnectionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { private Set edges; // if empty - all edges private Set notifyOn; @Override public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.EDGE_CONNECTIVITY; + return NotificationRuleTriggerType.EDGE_CONNECTION; } public enum EdgeConnectivityEvent { 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 ae2130a3f6..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,8 +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 = EdgeConnectivityNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTIVITY"), - @Type(value = EdgeFailureNotificationRuleTriggerConfig.class, name = "EDGE_FAILURE"), + @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 93d26140f0..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,8 +26,8 @@ public enum NotificationRuleTriggerType { ALARM_ASSIGNMENT, DEVICE_ACTIVITY, RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, - EDGE_CONNECTIVITY, - EDGE_FAILURE, + 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/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/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 3e02f42cba..b42fdd7645 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 @@ -189,8 +189,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.edgeConnectivity, tenantAdmins.getId()); - defaultNotifications.create(tenantId, DefaultNotifications.edgeFailure, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.edgeConnection, tenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.edgeCommunicationFailures, tenantAdmins.getId()); } @Override @@ -210,7 +210,7 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); } else { - List requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTIVITY, NotificationType.EDGE_FAILURE); + List requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE); List existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes( tenantId, requiredNotificationTypes, new PageLink(1)) .getData() @@ -227,11 +227,11 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS for (NotificationType type : requiredNotificationTypes) { if (!existingNotificationTypes.contains(type)) { switch (type) { - case EDGE_CONNECTIVITY: - defaultNotifications.create(tenantId, DefaultNotifications.edgeConnectivity, tenantAdmins.getId()); + case EDGE_CONNECTION: + defaultNotifications.create(tenantId, DefaultNotifications.edgeConnection, tenantAdmins.getId()); break; - case EDGE_FAILURE: - defaultNotifications.create(tenantId, DefaultNotifications.edgeFailure, tenantAdmins.getId()); + 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 4e2cc5bbad..1ef0006e87 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,9 +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.EdgeConnectivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectivityNotificationRuleTriggerConfig.EdgeConnectivityEvent; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeFailureNotificationRuleTriggerConfig; +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.EdgeCommunicationFailureNotificationRuleTriggerConfig; 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; @@ -328,34 +328,33 @@ 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 edgeConnectivity = DefaultNotification.builder() - .name("Edge connectivity notification") - .type(NotificationType.EDGE_CONNECTIVITY) - .subject("Edge '${edgeName}' is ${eventType}") + 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 connectivity") - .enabled(false) - .triggerConfig(EdgeConnectivityNotificationRuleTriggerConfig.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 Edge changes its connectivity state") + .description("Send notification to tenant admins when the connection status between TB and Edge changes") .build()) .build(); - public static final DefaultNotification edgeFailure = DefaultNotification.builder() - .name("Edge error notification") - .type(NotificationType.EDGE_FAILURE) - .subject("Edge '${edgeName}' received error") - .text("Error message: '${errorMsg}'") - .icon("error").color(null) + public static final DefaultNotification edgeCommunicationFailures = DefaultNotification.builder() + .name("Edge communication failure notification") + .type(NotificationType.EDGE_COMMUNICATION_FAILURE) + .subject("Edge '${edgeName}' communication failure occured") + .text("Failure message: '${failureMsg}'") + .icon("error").color(RED_COLOR) .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") .rule(DefaultRule.builder() - .name("Edge error") - .triggerConfig(EdgeFailureNotificationRuleTriggerConfig.builder().edges(null).build()) - .description("Send notification to tenant admins or to assigned customers to Edge when error occurs") + .name("Edge communication failure") + .triggerConfig(EdgeCommunicationFailureNotificationRuleTriggerConfig.builder().edges(null).build()) + .description("Send notification to tenant admins when communication failures occur") .build()) .build(); diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 852cb7a549..f4ae83143f 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -408,10 +408,10 @@ - + {{ 'notification.edge-trigger-settings' | translate }} -
+
notification.filter @@ -426,9 +426,9 @@ notification.notify-on - - {{ edgeConnectivityEventTranslationMap.get(edgeEvent) | translate }} + placeholder="{{ !edgeConnectionTemplateForm.get('triggerConfig.notifyOn').value?.length ? ('event.all-events' | translate) : '' }}"> + + {{ edgeConnectionEventTranslationMap.get(edgeEvent) | translate }} @@ -445,10 +445,10 @@ - + {{ 'notification.edge-trigger-settings' | translate }} -
+
notification.filter 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 099897af7c..34ff605027 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,7 +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 { EdgeConnectivityEvent, EdgeConnectivityEventTranslationMap } from '@shared/models/edge.models'; +import { EdgeConnectionEvent, EdgeConnectionEventTranslationMap } from '@shared/models/edge.models'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -99,8 +99,8 @@ export class RuleNotificationDialogComponent extends apiUsageLimitTemplateForm: FormGroup; newPlatformVersionTemplateForm: FormGroup; rateLimitsTemplateForm: FormGroup; - edgeFailureTemplateForm: FormGroup; - edgeConnectivityTemplateForm: FormGroup; + edgeCommunicationFailureTemplateForm: FormGroup; + edgeConnectionTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -135,8 +135,8 @@ export class RuleNotificationDialogComponent extends apiFeatures: ApiFeature[] = Object.values(ApiFeature); apiFeatureTranslationMap = ApiFeatureTranslationMap; - edgeConnectivityEvents: EdgeConnectivityEvent[] = Object.values(EdgeConnectivityEvent); - edgeConnectivityEventTranslationMap = EdgeConnectivityEventTranslationMap; + edgeConnectionEvents: EdgeConnectionEvent[] = Object.values(EdgeConnectionEvent); + edgeConnectionEventTranslationMap = EdgeConnectionEventTranslationMap; limitedApis: StringItemsOption[]; @@ -227,14 +227,14 @@ export class RuleNotificationDialogComponent extends } }); - this.edgeConnectivityTemplateForm = this.fb.group({ + this.edgeConnectionTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ edges: [null], notifyOn: [null] }) }); - this.edgeFailureTemplateForm = this.fb.group({ + this.edgeCommunicationFailureTemplateForm = this.fb.group({ triggerConfig: this.fb.group({ edges: [null] }) @@ -348,8 +348,8 @@ export class RuleNotificationDialogComponent extends [TriggerType.API_USAGE_LIMIT, this.apiUsageLimitTemplateForm], [TriggerType.NEW_PLATFORM_VERSION, this.newPlatformVersionTemplateForm], [TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm], - [TriggerType.EDGE_FAILURE, this.edgeFailureTemplateForm], - [TriggerType.EDGE_CONNECTIVITY, this.edgeConnectivityTemplateForm] + [TriggerType.EDGE_COMMUNICATION_FAILURE, this.edgeCommunicationFailureTemplateForm], + [TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm] ]); if (data.isAdd || data.isCopy) { diff --git a/ui-ngx/src/app/shared/models/edge.models.ts b/ui-ngx/src/app/shared/models/edge.models.ts index 4da5f089a0..569b2bec8a 100644 --- a/ui-ngx/src/app/shared/models/edge.models.ts +++ b/ui-ngx/src/app/shared/models/edge.models.ts @@ -191,14 +191,14 @@ export enum EdgeInstructionsMethod { export const edgeVersionAttributeKey = 'edgeVersion'; -export enum EdgeConnectivityEvent { +export enum EdgeConnectionEvent { CONNECTED= 'CONNECTED', DISCONNECTED = 'DISCONNECTED' } -export const EdgeConnectivityEventTranslationMap = new Map( +export const EdgeConnectionEventTranslationMap = new Map( [ - [EdgeConnectivityEvent.CONNECTED, 'edge-event.connected'], - [EdgeConnectivityEvent.DISCONNECTED, 'edge-event.disconnected'] + [EdgeConnectionEvent.CONNECTED, 'edge.connected'], + [EdgeConnectionEvent.DISCONNECTED, 'edge.disconnected'] ] ); diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index d23e6c5f84..ae945b243c 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -475,8 +475,8 @@ export enum NotificationType { NEW_PLATFORM_VERSION = 'NEW_PLATFORM_VERSION', RULE_NODE = 'RULE_NODE', RATE_LIMITS = 'RATE_LIMITS', - EDGE_CONNECTIVITY = 'EDGE_CONNECTIVITY', - EDGE_FAILURE = 'EDGE_FAILURE' + EDGE_CONNECTION = 'EDGE_CONNECTION', + EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE' } export const NotificationTypeIcons = new Map([ @@ -588,16 +588,16 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -628,8 +628,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_CONNECTIVITY, 'notification.trigger.edge-connectivity'], - [TriggerType.EDGE_FAILURE, 'notification.trigger.edge-failure'] + [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/assets/help/en_US/notification/edge_failure.md b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md similarity index 78% rename from ui-ngx/src/assets/help/en_US/notification/edge_failure.md rename to ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md index d0aaba21a0..03f2a2816a 100644 --- a/ui-ngx/src/assets/help/en_US/notification/edge_failure.md +++ b/ui-ngx/src/assets/help/en_US/notification/edge_communication_failure.md @@ -11,7 +11,7 @@ Available template parameters: * `edgeId` - the edge id as uuid string; * `edgeName` - the name of the edge; -* `errorMsg` - the string representation of the error, occurred on 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: @@ -28,14 +28,14 @@ Let's assume the notification about the failing of processing connection to Edge The following template: ```text -Edge '${edgeName}' received error +Edge '${edgeName}' communication failure occurred {:copy-code} ``` will be transformed to: ```text -Edge 'DatacenterEdge' received error +Edge 'DatacenterEdge' communication failure occurred ```
@@ -43,14 +43,14 @@ Edge 'DatacenterEdge' received error The following template: ```text -Error message: '${errorMsg}' +Failure message: '${failureMsg}' {:copy-code} ``` will be transformed to: ```text -Error message: 'Failed to process edge connection!' +Failure message: 'Failed to process edge connection!' ```
diff --git a/ui-ngx/src/assets/help/en_US/notification/edge_connectivity.md b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md similarity index 84% rename from ui-ngx/src/assets/help/en_US/notification/edge_connectivity.md rename to ui-ngx/src/assets/help/en_US/notification/edge_connection.md index 2044ecf708..fed6c0372e 100644 --- a/ui-ngx/src/assets/help/en_US/notification/edge_connectivity.md +++ b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md @@ -28,30 +28,17 @@ Let's assume the notification about the connecting Edge into the ThingsBoard. The following template: ```text -Edge '${edgeName}' is ${eventType} +Edge '${edgeName}' is now ${eventType} {:copy-code} ``` will be transformed to: ```text -Edge 'DatacenterEdge' is connected +Edge 'DatacenterEdge' is now connected ```
-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/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 63b791a6ef..74879d0420 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2030,7 +2030,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", @@ -2073,9 +2075,7 @@ "action-type-assigned-to-edge": "Assigned to Edge", "action-type-unassigned-from-edge": "Unassigned from Edge", "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request", - "connected": "Connected", - "disconnected": "Disconnected" + "action-type-entity-merge-request": "Entity Merge Request" }, "error": { "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", @@ -3430,8 +3430,8 @@ "rule-node": "Rule node", "new-platform-version": "New platform version", "rate-limits": "Exceeded rate limits", - "edge-failure": "Edge error", - "edge-connectivity": "Edge connectivity" + "edge-communication-failure": "Edge communication failure", + "edge-connection": "Edge connection" }, "templates": "Templates", "notification-templates": "Notifications / Templates", @@ -3452,8 +3452,8 @@ "rule-engine-lifecycle-event": "Rule engine lifecycle event", "new-platform-version": "New platform version", "rate-limits": "Exceeded rate limits", - "edge-connectivity": "Edge connectivity", - "edge-failure": "Edge failure", + "edge-connection": "Edge connection", + "edge-communication-failure": "Edge communication failure", "trigger": "Trigger", "trigger-required": "Trigger is required" }, From f22cf63f108517bd5c6ce045e29281fa3a0355bf Mon Sep 17 00:00:00 2001 From: Ivan Raznatovskyi Date: Wed, 24 Jan 2024 16:27:15 +0200 Subject: [PATCH 022/128] Changed validators for path fields --- .../widget/lib/gateway/gateway-configuration.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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( From a571153b7cf0826302030c0e6d50f7d52b8c375f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 24 Jan 2024 18:08:01 +0200 Subject: [PATCH 023/128] Single update message for multiple queues --- .../entitiy/queue/DefaultTbQueueService.java | 115 ++++++++---------- .../queue/DefaultTbClusterService.java | 66 +++++----- .../queue/DefaultTbCoreConsumerService.java | 10 +- .../DefaultTbRuleEngineConsumerService.java | 71 ++++++----- .../service/queue/TbCoreConsumerStats.java | 4 +- .../discovery/HashPartitionServiceTest.java | 6 +- .../queue/DefaultTbClusterServiceTest.java | 9 +- .../server/queue/TbQueueClusterService.java | 8 +- common/proto/src/main/proto/queue.proto | 12 +- .../queue/discovery/HashPartitionService.java | 41 ++++--- .../queue/discovery/PartitionService.java | 4 +- .../service/DefaultTransportService.java | 13 +- .../server/dao/queue/BaseQueueService.java | 3 - 13 files changed, 184 insertions(+), 178 deletions(-) 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 b0ef8f4cde..e13342eee0 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/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 14930912d1..1f27f06502 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 @@ -59,6 +59,8 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; 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; @@ -68,8 +70,8 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper; 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.TopicService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; @@ -77,9 +79,11 @@ import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; +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; @@ -552,40 +556,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 8e5fde9ae8..f544c4fe25 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 8cc477999f..6a205ed5a2 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 5ff72e97e3..da3171bfc0 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/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index 4ea648119b..669b3dda4a 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/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 233839bb5f..57bf94fec3 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 @@ -40,6 +40,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; @@ -92,7 +93,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()); @@ -117,7 +118,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); @@ -145,7 +146,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); @@ -191,7 +192,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/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 c852a24fa7..49017df678 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/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index fea4848654..b1362bf27c 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 bfc50e076b..acb26842d5 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,36 @@ 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()); + myPartitions.remove(queueKey); + 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()))); } } 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 e9049a8085..27417e5f20 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/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 6f962ca269..2d6ab0fb8b 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 @@ -50,9 +50,9 @@ 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.limit.LimitedApi; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -96,10 +96,9 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.AsyncCallbackTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.QueueKey; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbTransportQueueFactory; import org.thingsboard.server.queue.scheduler.SchedulerComponent; @@ -1056,10 +1055,10 @@ public class DefaultTransportService implements TransportService { 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/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueService.java index c1b0c66875..1bf9e25fc0 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); From 167d8758f6c1d16f6e45945ac13be7495e406913 Mon Sep 17 00:00:00 2001 From: artem Date: Wed, 24 Jan 2024 18:28:17 +0200 Subject: [PATCH 024/128] TbGpsGeofencingActionNode: renamed key to reportPresenceStatusOnEachMessage + removed told variable + tests refactored --- .../engine/geo/TbGpsGeofencingActionNode.java | 46 ++++--- ...bGpsGeofencingActionNodeConfiguration.java | 4 +- .../geo/GpsGeofencingActionTestCase.java | 38 ++++++ .../geo/TbGpsGeofencingActionNodeTest.java | 117 ++++++++---------- 4 files changed, 120 insertions(+), 85 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index cd88a1eda7..0a9d0deb1d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -62,14 +62,16 @@ import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE; "

" + "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 with types Inside or Outside only the first time the geofencing and duration conditions are satisfied; otherwise Success. " + - "If the presence monitoring strategy \"On each message\" is selected, sends messages with types Inside or Outside every time the geofencing condition is satisfied.", + "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 PRESENCE_MONITORING_STRATEGY_ON_EACH_MESSAGE = "presenceMonitoringStrategyOnEachMessage"; + 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(); @@ -95,28 +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; - } - } - } else { + return; + } + + if (config.isReportPresenceStatusOnEachMessage()) { ctx.tellNext(msg, entityState.isInside() ? INSIDE : OUTSIDE); - told = true; + 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) { @@ -150,9 +156,9 @@ public class TbGpsGeofencingActionNode extends AbstractGeofencingNode upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { boolean hasChanges = false; if (fromVersion == 0) { - if (!oldConfiguration.has(PRESENCE_MONITORING_STRATEGY_ON_EACH_MESSAGE)) { + if (!oldConfiguration.has(REPORT_PRESENCE_STATUS_ON_EACH_MESSAGE)) { hasChanges = true; - ((ObjectNode) oldConfiguration).put(PRESENCE_MONITORING_STRATEGY_ON_EACH_MESSAGE, false); + ((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 ee375083b4..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,7 +31,7 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte private String minInsideDurationTimeUnit; private String minOutsideDurationTimeUnit; - private boolean presenceMonitoringStrategyOnEachMessage; + private boolean reportPresenceStatusOnEachMessage; @Override public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() { @@ -45,7 +45,7 @@ public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilte configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name()); configuration.setMinInsideDuration(1); configuration.setMinOutsideDuration(1); - configuration.setPresenceMonitoringStrategyOnEachMessage(false); + configuration.setReportPresenceStatusOnEachMessage(true); return configuration; } } 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..8444c9cf0d --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java @@ -0,0 +1,38 @@ +/** + * 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 index dd7536f4ee..42d69f2593 100644 --- 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 @@ -24,6 +24,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; @@ -37,8 +38,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.attributes.AttributesService; -import java.util.List; -import java.util.Map; +import java.time.Duration; import java.util.Optional; import java.util.UUID; import java.util.stream.Stream; @@ -51,6 +51,8 @@ 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; class TbGpsGeofencingActionNodeTest { @@ -70,42 +72,47 @@ class TbGpsGeofencingActionNodeTest { node.destroy(); } - private static Stream givenPresenceMonitoringStrategyOnEachMessage_whenOnMsg_thenVerifyOutputMsgTypes() { + 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 - Arguments.of(false, List.of( - Map.of(ENTERED, 0, INSIDE, 0, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 1), - Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 1), - Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 2) - )), - // default config with presenceMonitoringStrategyOnEachMessage true - Arguments.of(true, List.of( - Map.of(ENTERED, 0, INSIDE, 0, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 0, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 1, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 2, SUCCESS, 0), - Map.of(ENTERED, 1, INSIDE, 3, SUCCESS, 0) - )) + // 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 givenPresenceMonitoringStrategyOnEachMessage_whenOnMsg_thenVerifyOutputMsgTypes( - boolean presenceMonitoringStrategyOnEachMessage, - List> outputMsgTypesCountList + void givenReportPresenceStatusOnEachMessage_whenOnMsg_thenVerifyOutputMsgType( + GpsGeofencingActionTestCase gpsGeofencingActionTestCase, + String expectedOutput ) throws TbNodeException { // GIVEN var config = new TbGpsGeofencingActionNodeConfiguration().defaultConfiguration(); - config.setPresenceMonitoringStrategyOnEachMessage(presenceMonitoringStrategyOnEachMessage); + config.setReportPresenceStatusOnEachMessage(gpsGeofencingActionTestCase.isReportPresenceStatusOnEachMessage()); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); - DeviceId deviceId = new DeviceId(UUID.randomUUID()); - TbMsgMetaData metadata = getMetadataForNewVersionPolygonPerimeter(); - TbMsg msg = getTbMsg(deviceId, metadata, - GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + TbMsg msg = gpsGeofencingActionTestCase.isMsgInside() ? + getInsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()) : + getOutsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()); when(ctx.getAttributesService()).thenReturn(attributesService); when(ctx @@ -113,40 +120,30 @@ class TbGpsGeofencingActionNodeTest { .find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getServiceId())) .thenReturn(Futures.immediateFuture(Optional.empty())); - // WHEN - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - node.onMsg(ctx, msg); - - // THEN - verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(0)); + ReflectionTestUtils.setField(node, "entityStates", gpsGeofencingActionTestCase.getEntityStates()); // WHEN - msg = getTbMsg(deviceId, metadata, - GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); - node.onMsg(ctx, msg); - - // THEN - verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(1)); - - // WHEN - node.onMsg(ctx, msg); - - // THEN - verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(2)); - - // WHEN - config.setMinInsideDuration(0); - node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); node.onMsg(ctx, msg); // THEN - verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(3)); + if (SUCCESS.equals(expectedOutput)) { + verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + } else { + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(expectedOutput)); + } + } - // WHEN - node.onMsg(ctx, msg); + private TbMsg getOutsideRectangleTbMsg(EntityId entityId) { + return getTbMsg(entityId, getMetadataForNewVersionPolygonPerimeter(), + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + } - // THEN - verifyNodeOutputs(newMsgCaptor, outputMsgTypesCountList.get(4)); + 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) { @@ -160,12 +157,6 @@ class TbGpsGeofencingActionNodeTest { return metadata; } - private void verifyNodeOutputs(ArgumentCaptor newMsgCaptor, Map outputMsgTypesCount) { - verify(this.ctx, times(outputMsgTypesCount.get(ENTERED))).tellNext(newMsgCaptor.capture(), eq(ENTERED)); - verify(this.ctx, times(outputMsgTypesCount.get(INSIDE))).tellNext(newMsgCaptor.capture(), eq(INSIDE)); - verify(this.ctx, times(outputMsgTypesCount.get(SUCCESS))).tellSuccess(newMsgCaptor.capture()); - } - // Rule nodes upgrade private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { return Stream.of( @@ -193,7 +184,7 @@ class TbGpsGeofencingActionNodeTest { " \"minOutsideDuration\": 1,\n" + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + - " \"presenceMonitoringStrategyOnEachMessage\": false,\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + " \"latitudeKeyName\": \"latitude\",\n" + " \"longitudeKeyName\": \"longitude\",\n" + " \"perimeterType\": \"POLYGON\",\n" + @@ -212,7 +203,7 @@ class TbGpsGeofencingActionNodeTest { " \"minOutsideDuration\": 1,\n" + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + - " \"presenceMonitoringStrategyOnEachMessage\": false,\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + " \"latitudeKeyName\": \"latitude\",\n" + " \"longitudeKeyName\": \"longitude\",\n" + " \"perimeterType\": \"POLYGON\",\n" + @@ -230,7 +221,7 @@ class TbGpsGeofencingActionNodeTest { " \"minOutsideDuration\": 1,\n" + " \"minInsideDurationTimeUnit\": \"MINUTES\",\n" + " \"minOutsideDurationTimeUnit\": \"MINUTES\",\n" + - " \"presenceMonitoringStrategyOnEachMessage\": false,\n" + + " \"reportPresenceStatusOnEachMessage\": false,\n" + " \"latitudeKeyName\": \"latitude\",\n" + " \"longitudeKeyName\": \"longitude\",\n" + " \"perimeterType\": \"POLYGON\",\n" + From 54203ea1775ac91c402362e975babfb003f5552e Mon Sep 17 00:00:00 2001 From: artem Date: Wed, 24 Jan 2024 19:19:10 +0200 Subject: [PATCH 025/128] TbGpsGeofencingActionNodeTest: added asserts and additional verifications --- .../geo/TbGpsGeofencingActionNodeTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 index 42d69f2593..d61688d5a9 100644 --- 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 @@ -44,8 +44,10 @@ import java.util.UUID; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -123,15 +125,27 @@ class TbGpsGeofencingActionNodeTest { ReflectionTestUtils.setField(node, "entityStates", gpsGeofencingActionTestCase.getEntityStates()); // WHEN - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); node.onMsg(ctx, msg); // THEN + verify(ctx, never()).tellFailure(any(), any(Throwable.class)); + verify(ctx, never()).enqueueForTellNext(any(), eq(expectedOutput), any(), any()); + verify(ctx, never()).ack(any()); + + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); if (SUCCESS.equals(expectedOutput)) { verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); } else { verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(expectedOutput)); } + + var actualMsg = newMsgCaptor.getValue(); + assertThat(actualMsg).isNotNull(); + assertThat(actualMsg).isSameAs(msg); + assertThat(actualMsg.getType()).isSameAs(msg.getType()); + assertThat(actualMsg.getData()).isSameAs(msg.getData()); + assertThat(actualMsg.getMetaData()).isEqualTo(msg.getMetaData()); + assertThat(actualMsg.getOriginator()).isSameAs(msg.getOriginator()); } private TbMsg getOutsideRectangleTbMsg(EntityId entityId) { From 27f448a5430220ca8c6acb41449cc7cdcd4496ec Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 25 Jan 2024 12:04:18 +0200 Subject: [PATCH 026/128] Improve partition service logs --- .../queue/discovery/HashPartitionService.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 acb26842d5..2d0e03ecf9 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 @@ -325,13 +325,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()); @@ -377,14 +375,11 @@ 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()))); applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceType, partitionsMap)); } @@ -489,7 +484,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) { From 7bd55e5838fa3d2c939778a5ff8f4ec513967783 Mon Sep 17 00:00:00 2001 From: Ivan Raznatovskyi Date: Thu, 25 Jan 2024 12:20:40 +0200 Subject: [PATCH 027/128] Fixed toast popup position after Gateway connector saving --- .../widget/lib/gateway/gateway-connectors.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..f47bb748a9 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 })); From ac2da12054266b313b5b56765411cdc242df45f8 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 25 Jan 2024 12:24:04 +0200 Subject: [PATCH 028/128] UI: Update rule core config --- .../static/rulenode/rulenode-core-config.js | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) 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 From e0348751ad5e8514134f7d60ab45d86d247ceb63 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 25 Jan 2024 12:58:27 +0200 Subject: [PATCH 029/128] UI: added html reponse improvement for snmp transport config --- ...ice-profile-communication-config.component.html | 2 +- ...ice-profile-communication-config.component.scss | 14 ++++++++++++++ .../snmp-device-profile-mapping.component.html | 2 +- .../snmp-device-profile-mapping.component.scss | 10 +++------- 4 files changed, 19 insertions(+), 9 deletions(-) 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..d3908cb6bf 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 @@ -19,6 +19,13 @@ border-radius: 4px; padding: 8px; min-width: 0; + flex-direction: row; + box-sizing: border-box; + display: flex; + place-content: stretch flex-start; + align-items: stretch; + flex: 1 1 0; + gap: 8px; } .scope-row { @@ -28,6 +35,13 @@ .required-text { margin: 16px 0 } + + @media (max-width: 1500px) { + .communication-config { + flex-direction: column; + gap: 0; + } + } } :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..94330f7c23 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 @@ -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; + } } } From 2c07a5ea929fd97471ab80ee6ed5a98130b9e13c Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 25 Jan 2024 13:23:12 +0200 Subject: [PATCH 030/128] minor edition for media query --- .../snmp-device-profile-communication-config.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d3908cb6bf..870b627378 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 @@ -36,7 +36,7 @@ margin: 16px 0 } - @media (max-width: 1500px) { + @media (max-width: 1650px) { .communication-config { flex-direction: column; gap: 0; From fec8c5695ddcc332538be6bae1ba8b464f40bf96 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 25 Jan 2024 13:32:33 +0200 Subject: [PATCH 031/128] UI: minor improvements for styles and timewindow scss --- .../app/shared/components/time/timewindow.component.scss | 6 ++++++ ui-ngx/src/styles.scss | 4 ++++ 2 files changed, 10 insertions(+) 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..d793bc5367 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,12 @@ .mdc-button__label { overflow: hidden; text-overflow: ellipsis; + height: 100%; + + span { + display: block; + height: 100%; + } } } } 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; + } } From 2f7351200504126c159cb7ef920eb447c8ce7f36 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 25 Jan 2024 15:47:26 +0200 Subject: [PATCH 032/128] Don't remove from myPartitions on queue update --- .../thingsboard/server/queue/discovery/HashPartitionService.java | 1 - 1 file changed, 1 deletion(-) 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 2d0e03ecf9..9b4cf0e974 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 @@ -177,7 +177,6 @@ public class HashPartitionService implements PartitionService { 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); } From 8f9eddabbae1ddd2b0dd4ea2868cc1361d5e93d0 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 25 Jan 2024 16:00:44 +0200 Subject: [PATCH 033/128] UI: added singletonMode to copy buffer of rule-node --- ui-ngx/src/app/core/services/item-buffer.service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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..b928245765 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,9 @@ export class ItemBufferService { if (origNode.error) { node.error = origNode.error; } + if (isDefinedAndNotNull(origNode.singletonMode)) { + node.singletonMode = origNode.singletonMode; + } ruleNodes.nodes.push(node); if (i === 0) { top = node.y; From b635e598857f49fa17384b0229785f1e24d45c4d Mon Sep 17 00:00:00 2001 From: Yevhen Popok Date: Fri, 26 Jan 2024 09:56:03 +0200 Subject: [PATCH 034/128] For Angular Gridster increase "maxRows" limit to "Infinity" --- .../modules/home/components/dashboard/dashboard.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..6fa26ee412 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 @@ -211,7 +211,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo disableAutoPositionOnConflict: false, pushItems: false, swap: false, - maxRows: 100, + maxRows: Infinity, minCols: this.columns ? this.columns : 24, maxCols: 3000, maxItemCols: 1000, From 3ee8a8c56d10d5a08111032c6d90067d6a538535 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 26 Jan 2024 12:17:37 +0200 Subject: [PATCH 035/128] Refactor rate limit service --- .../config/RateLimitProcessingFilter.java | 2 +- .../server/controller/AuthController.java | 2 +- .../controller/plugin/TbWebSocketHandler.java | 2 +- .../DefaultNotificationCenter.java | 2 +- .../DefaultNotificationRuleProcessor.java | 2 +- .../auth/mfa/DefaultTwoFactorAuthService.java | 2 +- .../DefaultEntitiesExportImportService.java | 2 +- .../service/limits/RateLimitServiceTest.java | 10 ++++---- .../notification/NotificationRuleApiTest.java | 2 +- common/cache/pom.xml | 4 +++ .../limits/DefaultRateLimitService.java | 16 ++++++------ .../cache}/limits/RateLimitService.java | 2 +- .../cache/limits/TenantProfileProvider.java | 25 +++++++++++++++++++ .../CassandraBufferedRateReadExecutor.java | 2 +- .../CassandraBufferedRateWriteExecutor.java | 2 +- .../tenant/DefaultTbTenantProfileCache.java | 3 ++- .../util/AbstractBufferedRateExecutor.java | 2 +- 17 files changed, 55 insertions(+), 27 deletions(-) rename {dao/src/main/java/org/thingsboard/server/dao/util => common/cache/src/main/java/org/thingsboard/server/cache}/limits/DefaultRateLimitService.java (89%) rename {dao/src/main/java/org/thingsboard/server/dao/util => common/cache/src/main/java/org/thingsboard/server/cache}/limits/RateLimitService.java (95%) create mode 100644 common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java 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/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/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index be37d98bb5..11c40d964a 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/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/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/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 9917e837b9..afdb38a2ec 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()); } 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/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 89% 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..a8f4545449 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,14 +13,13 @@ * 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; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; @@ -28,10 +27,9 @@ 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; @@ -39,14 +37,14 @@ import java.util.concurrent.TimeUnit; @Slf4j public class DefaultRateLimitService implements RateLimitService { - private final TbTenantProfileCache tenantProfileCache; + private final TenantProfileProvider tenantProfileProvider; private final NotificationRuleProcessor notificationRuleProcessor; - public DefaultRateLimitService(TbTenantProfileCache tenantProfileCache, - @Lazy NotificationRuleProcessor notificationRuleProcessor, + public DefaultRateLimitService(TenantProfileProvider tenantProfileProvider, + 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 +64,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/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java b/common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java new file mode 100644 index 0000000000..15243d12c8 --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/limits/TenantProfileProvider.java @@ -0,0 +1,25 @@ +/** + * 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.cache.limits; + +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantId; + +public interface TenantProfileProvider { + + TenantProfile get(TenantId tenantId); + +} 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/tenant/DefaultTbTenantProfileCache.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/DefaultTbTenantProfileCache.java index 4610973370..a9d118befb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/DefaultTbTenantProfileCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/DefaultTbTenantProfileCache.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.tenant; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.limits.TenantProfileProvider; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.EntityId; @@ -31,7 +32,7 @@ import java.util.function.Consumer; @Service @Slf4j -public class DefaultTbTenantProfileCache implements TbTenantProfileCache { +public class DefaultTbTenantProfileCache implements TbTenantProfileCache, TenantProfileProvider { private final Lock tenantProfileFetchLock = new ReentrantLock(); private final TenantProfileService tenantProfileService; 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; From a729967a930f4ee9de8fde68a046bb4b08575aad Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 26 Jan 2024 15:21:27 +0200 Subject: [PATCH 036/128] Fix DefaultRateLimitService init --- .../server/cache/limits/DefaultRateLimitService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java b/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java index a8f4545449..f3530e99d2 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/limits/DefaultRateLimitService.java @@ -20,6 +20,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; @@ -33,6 +34,7 @@ import org.thingsboard.server.common.msg.tools.TbRateLimits; import java.util.concurrent.TimeUnit; +@Lazy @Service @Slf4j public class DefaultRateLimitService implements RateLimitService { @@ -41,7 +43,7 @@ public class DefaultRateLimitService implements RateLimitService { private final NotificationRuleProcessor notificationRuleProcessor; public DefaultRateLimitService(TenantProfileProvider tenantProfileProvider, - NotificationRuleProcessor notificationRuleProcessor, + @Lazy NotificationRuleProcessor notificationRuleProcessor, @Value("${cache.rateLimits.timeToLiveInMinutes:120}") int rateLimitsTtl, @Value("${cache.rateLimits.maxSize:200000}") int rateLimitsCacheMaxSize) { this.tenantProfileProvider = tenantProfileProvider; From 3fcb913645762f0925fe02b0732fd68dcd656ee1 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 26 Jan 2024 17:54:24 +0200 Subject: [PATCH 037/128] UI: Update Widget action form style. --- ui-ngx/src/app/modules/common/modules-map.ts | 12 +- .../home/components/home-components.module.ts | 9 - .../manage-widget-actions.component.models.ts | 30 +- .../widget-action-dialog.component.html | 280 +++-------- .../action/widget-action-dialog.component.ts | 350 +------------ ...custom-action-pretty-editor.component.html | 3 +- ...custom-action-pretty-editor.component.scss | 2 +- .../custom-action-pretty-editor.component.ts | 4 +- ...ction-pretty-resources-tabs.component.html | 1 + ...ction-pretty-resources-tabs.component.scss | 0 ...-action-pretty-resources-tabs.component.ts | 2 +- .../action/custom-action.models.ts | 24 + .../{ => config}/action/custom-sample-css.raw | 0 .../action/custom-sample-html.raw | 0 .../{ => config}/action/custom-sample-js.raw | 0 .../mobile-action-editor.component.html | 56 ++- .../action/mobile-action-editor.component.ts | 35 +- .../action/mobile-action-editor.models.ts | 40 +- .../action/widget-action.component.html | 234 +++++++++ .../config/action/widget-action.component.ts | 463 ++++++++++++++++++ .../config/widget-config-components.module.ts | 17 +- .../common/css-size-input.component.html | 33 ++ .../common/css-size-input.component.ts | 124 +++++ .../common/css-unit-select.component.html | 2 +- .../common/css-unit-select.component.ts | 3 + .../common/widget-settings-common.module.ts | 3 + .../components/widget/widget.component.ts | 2 +- .../dashboard-autocomplete.component.html | 24 +- .../dashboard-autocomplete.component.ts | 40 +- .../shared/components/js-func.component.html | 12 +- .../shared/components/js-func.component.scss | 6 +- .../shared/components/js-func.component.ts | 20 +- .../shared/models/widget-settings.models.ts | 4 +- ui-ngx/src/app/shared/models/widget.models.ts | 24 +- .../assets/locale/locale.constant-ca_ES.json | 6 +- .../assets/locale/locale.constant-cs_CZ.json | 2 +- .../assets/locale/locale.constant-da_DK.json | 2 +- .../assets/locale/locale.constant-de_DE.json | 2 +- .../assets/locale/locale.constant-el_GR.json | 2 +- .../assets/locale/locale.constant-en_US.json | 19 +- .../assets/locale/locale.constant-es_ES.json | 6 +- .../assets/locale/locale.constant-fa_IR.json | 2 +- .../assets/locale/locale.constant-fr_FR.json | 2 +- .../assets/locale/locale.constant-it_IT.json | 2 +- .../assets/locale/locale.constant-ja_JP.json | 2 +- .../assets/locale/locale.constant-ka_GE.json | 2 +- .../assets/locale/locale.constant-ko_KR.json | 2 +- .../assets/locale/locale.constant-lv_LV.json | 2 +- .../assets/locale/locale.constant-nl_BE.json | 6 +- .../assets/locale/locale.constant-pt_BR.json | 2 +- .../assets/locale/locale.constant-ro_RO.json | 2 +- .../assets/locale/locale.constant-sl_SI.json | 2 +- .../assets/locale/locale.constant-tr_TR.json | 2 +- .../assets/locale/locale.constant-uk_UA.json | 2 +- .../assets/locale/locale.constant-zh_CN.json | 6 +- .../assets/locale/locale.constant-zh_TW.json | 6 +- ui-ngx/src/form.scss | 6 +- 57 files changed, 1217 insertions(+), 729 deletions(-) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-editor.component.html (94%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-editor.component.scss (96%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-editor.component.ts (96%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-resources-tabs.component.html (98%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-resources-tabs.component.scss (100%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action-pretty-resources-tabs.component.ts (99%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-action.models.ts (71%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-sample-css.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-sample-html.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/custom-sample-js.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/mobile-action-editor.component.html (74%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/mobile-action-editor.component.ts (91%) rename ui-ngx/src/app/modules/home/components/widget/{ => config}/action/mobile-action-editor.models.ts (92%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-size-input.component.ts diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index c19b96e2a7..f8dab27af9 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -226,9 +226,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/config/action/custom-action-pretty-resources-tabs.component'; +import * as CustomActionPrettyEditorComponent from '@home/components/widget/config/action/custom-action-pretty-editor.component'; +import * as MobileActionEditorComponent from '@home/components/widget/config/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'; @@ -541,9 +541,9 @@ 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/config/action/custom-action-pretty-resources-tabs.component': CustomActionPrettyResourcesTabsComponent, + '@home/components/widget/config/action/custom-action-pretty-editor.component': CustomActionPrettyEditorComponent, + '@home/components/widget/config/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, 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/widget/action/manage-widget-actions.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.models.ts index 941affd220..428fb6fa1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.models.ts @@ -15,7 +15,6 @@ /// import { - CustomActionDescriptor, WidgetActionDescriptor, WidgetActionSource, widgetActionTypeTranslationMap @@ -27,11 +26,7 @@ import { TranslateService } from '@ngx-translate/core'; import { PageLink } from '@shared/models/page/page-link'; import { catchError, map, publishReplay, refCount } from 'rxjs/operators'; import { UtilsService } from '@core/services/utils.service'; -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'; +import { deepClone } from '@core/utils'; export interface WidgetActionCallbacks { fetchDashboardStates: (query: string) => 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..1eec44b714 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,38 @@ /// 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, 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/config/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 +55,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 +62,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 +92,11 @@ export class WidgetActionDialogComponent extends DialogComponent { - this.updateActionTypeFormGroup(type); - }); this.widgetActionFormGroup.get('actionSourceId').valueChanges.pipe( takeUntil(this.destroy$) ).subscribe(() => { @@ -209,233 +163,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 +201,16 @@ export class WidgetActionDialogComponent extends DialogComponent -
+
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/config/action/custom-action-pretty-editor.component.scss similarity index 96% 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/config/action/custom-action-pretty-editor.component.scss index 6af29b3cff..4087b33aa3 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/config/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/config/action/custom-action-pretty-editor.component.ts similarity index 96% 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/config/action/custom-action-pretty-editor.component.ts index 47178013ae..d18ec34183 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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/action/custom-action-pretty-resources-tabs.component.ts index 71bb0a4a2e..a5beaa7f7a 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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/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/config/action/custom-sample-js.raw 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/config/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/config/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/config/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/config/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/config/action/mobile-action-editor.component.ts index af028ac0ae..a95e34bec9 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/config/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/config/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/config/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/config/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/config/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/config/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/config/action/widget-action.component.html b/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.html new file mode 100644 index 0000000000..35ca3f3144 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.html @@ -0,0 +1,234 @@ + +
+
+
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.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/config/action/widget-action.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts new file mode 100644 index 0000000000..ed7690fe28 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts @@ -0,0 +1,463 @@ +/// +/// 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/config/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; + } + } + 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/config/widget-config-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts index 4470fc1327..7b80b4b088 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts @@ -33,6 +33,11 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component'; import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; import { TargetDeviceComponent } from '@home/components/widget/config/target-device.component'; +import { WidgetActionComponent } from '@home/components/widget/config/action/widget-action.component'; +import { CustomActionPrettyResourcesTabsComponent } + from '@home/components/widget/config/action/custom-action-pretty-resources-tabs.component'; +import { CustomActionPrettyEditorComponent } from '@home/components/widget/config/action/custom-action-pretty-editor.component'; +import { MobileActionEditorComponent } from '@home/components/widget/config/action/mobile-action-editor.component'; @NgModule({ declarations: @@ -50,7 +55,11 @@ import { TargetDeviceComponent } from '@home/components/widget/config/target-dev TimewindowStyleComponent, TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - WidgetSettingsComponent + WidgetSettingsComponent, + WidgetActionComponent, + CustomActionPrettyResourcesTabsComponent, + CustomActionPrettyEditorComponent, + MobileActionEditorComponent ], imports: [ CommonModule, @@ -73,7 +82,11 @@ import { TargetDeviceComponent } from '@home/components/widget/config/target-dev TimewindowStylePanelComponent, TimewindowConfigPanelComponent, WidgetSettingsComponent, - WidgetSettingsCommonModule + WidgetSettingsCommonModule, + WidgetActionComponent, + CustomActionPrettyResourcesTabsComponent, + CustomActionPrettyEditorComponent, + MobileActionEditorComponent ] }) export class WidgetConfigComponentsModule { } 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/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index ba08a39bb7..8275f53e54 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 @@ -66,6 +66,7 @@ import { import { RpcUpdateStateSettingsPanelComponent } from '@home/components/widget/lib/settings/common/rpc/rpc-update-state-settings-panel.component'; +import { CssSizeInputComponent } from '@home/components/widget/lib/settings/common/css-size-input.component'; @NgModule({ declarations: [ @@ -76,6 +77,7 @@ import { ColorSettingsComponent, ColorSettingsPanelComponent, CssUnitSelectComponent, + CssSizeInputComponent, DateFormatSelectComponent, DateFormatSettingsPanelComponent, BackgroundSettingsComponent, @@ -106,6 +108,7 @@ import { ColorSettingsComponent, ColorSettingsPanelComponent, CssUnitSelectComponent, + CssSizeInputComponent, DateFormatSelectComponent, DateFormatSettingsPanelComponent, BackgroundSettingsComponent, 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..1c029b6524 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 @@ -1345,7 +1345,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, {} ); 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..5d41da6b4a 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/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. --> -
- - +
-
widgets.rpc-state.turn-off
- widgets.value-action.turn-off
+ + formControlName="offUpdateState">
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..82c2766a23 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; 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..8557c9cbd3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts @@ -0,0 +1,659 @@ +/// +/// 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, + GetTelemetryValueSettings, + 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()); + + error = ''; + + 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()); + } + + public onInit() { + } + + public clearError() { + this.error = ''; + this.cd.markForCheck(); + } + + protected createValueGetter(getValueSettings: GetValueSettings, + valueType: ValueType, + valueObserver?: Partial>): ValueGetter { + const observer: Partial> = { + next: (value: V) => { + if (valueObserver?.next) { + valueObserver.next(value); + } + }, + error: (err: any) => { + this.onError(err); + 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.error = error; + this.cd.markForCheck(); + } + + 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.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.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); + } + if (this.getTelemetryValueSettings().subscribeForUpdates) { + return this.subscribeForTelemetryValue(); + } else { + return this.doGetTelemetryValue(); + } + } 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; + + protected abstract doGetTelemetryValue(): Observable; + + 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; + } + + protected doGetTelemetryValue(): Observable { + const getAttributeValueSettings = this.getTelemetryValueSettings(); + return this.ctx.attributeService.getEntityAttributes(this.targetEntityId, + getAttributeValueSettings.scope, [getAttributeValueSettings.key], {ignoreLoading: true, ignoreErrors: true}).pipe( + map((data) => data.find(attr => attr.key === getAttributeValueSettings.key)?.value) + ); + } + +} + +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(): GetTelemetryValueSettings { + return this.settings.getTimeSeries; + } + + protected doGetTelemetryValue(): Observable { + const getTelemetryValueSettings = this.getTelemetryValueSettings(); + return this.ctx.attributeService.getEntityTimeseriesLatest(this.targetEntityId, + [getTelemetryValueSettings.key], true, {ignoreLoading: true, ignoreErrors: true}) + .pipe( + map((data) => { + let value: any = null; + if (data[getTelemetryValueSettings.key]) { + const dataSet = data[getTelemetryValueSettings.key]; + if (dataSet.length) { + value = dataSet[0].value; + } + } + return value; + }) + ); + } + +} + +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/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..04ec9f22ab 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 @@ -27,7 +27,7 @@
{{ offLabel }}
- +
{{ onLabel }}
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..61d4fb9322 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,20 +25,25 @@ 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'; const horizontalLayoutPadding = 48; @@ -51,7 +56,7 @@ const verticalLayoutPadding = 36; 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,13 @@ export class SingleSwitchWidgetComponent extends @ViewChild('singleSwitchToggleRow', {static: false}) singleSwitchToggleRow: ElementRef; + settings: SingleSwitchWidgetSettings; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + value = false; + layout: SingleSwitchLayout; showIcon = false; @@ -87,16 +99,24 @@ export class SingleSwitchWidgetComponent extends private panelResize$: ResizeObserver; + private onValueSetter: ValueSetter; + private offValueSetter: ValueSetter; + constructor(protected imagePipe: ImagePipe, protected sanitizer: DomSanitizer, private renderer: Renderer2, 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; @@ -134,6 +154,20 @@ export class SingleSwitchWidgetComponent extends cssParser.cssPreviewNamespace = namespace; cssParser.createStyleElement(namespace, switchVariablesCss); this.renderer.addClass(this.elementRef.nativeElement, namespace); + + const getInitialStateSettings = + {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.value-action.initial-state')}; + this.createValueGetter(getInitialStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onValue(value) + }); + + const onUpdateStateSettings = {...this.settings.onUpdateState, + actionLabel: this.ctx.translate.instant('widgets.value-action.turn-on')}; + this.onValueSetter = this.createValueSetter(onUpdateStateSettings); + + const offUpdateStateSettings = {...this.settings.offUpdateState, + actionLabel: this.ctx.translate.instant('widgets.value-action.turn-off')}; + this.offValueSetter = this.createValueSetter(offUpdateStateSettings); } ngAfterViewInit(): void { @@ -156,31 +190,30 @@ export class SingleSwitchWidgetComponent extends if (this.panelResize$) { this.panelResize$.disconnect(); } + super.ngOnDestroy(); } - protected stateValueType(): ValueType { - return ValueType.BOOLEAN; - } - - protected defaultValue(): boolean { - return false; - } - - protected defaultSettings(): SingleSwitchWidgetSettings { - return {...singleSwitchDefaultSettings}; - } - - protected initialState(): RpcInitialStateSettings { - return {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.initial-state')}; + public onInit() { + super.onInit(); + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); } - 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')}; + public onToggleChange(event: MouseEvent) { + 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 validateValue(value: any): boolean { - return !!value; + private onValue(value: boolean): void { + console.log(`onValue: ${value}`); + this.value = !!value; + this.cd.markForCheck(); } private onResize() { 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..23510e174a 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,10 @@ export const singleSwitchLayoutImages = new Map( ] ); -export interface SingleSwitchWidgetSettings extends RpcStateWidgetSettings { - onUpdateState: RpcUpdateStateSettings; - offUpdateState: RpcUpdateStateSettings; +export interface SingleSwitchWidgetSettings { + initialState: GetValueSettings; + onUpdateState: SetValueSettings; + offUpdateState: SetValueSettings; layout: SingleSwitchLayout; autoScale: boolean; showLabel: boolean; @@ -77,11 +78,12 @@ export interface SingleSwitchWidgetSettings extends RpcStateWidgetSettings -
-
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,58 +71,68 @@
-
{{ 'widgets.rpc-state.attribute-key' | translate }}*
+
{{ 'widgets.value-action.attribute-key' | translate }}*
+ [attributeScope]="getValueSettingsFormGroup.get('getAttribute').get('scope').value">
+
+ + {{ 'widgets.value-action.subscribe-for-updates' | translate }} + +
- +
-
{{ 'widgets.rpc-state.time-series-key' | translate }}*
+
{{ 'widgets.value-action.time-series-key' | translate }}*
+
+ + {{ 'widgets.value-action.subscribe-for-updates' | 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.on-when-result-is
-
@@ -142,16 +152,16 @@
-
{{ 'widgets.rpc-state.request-timeout-ms' | translate }}
+
{{ 'widgets.value-action.request-timeout-ms' | translate }}
warning @@ -159,28 +169,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 +203,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..8f40d59bd1 --- /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,182 @@ +/// +/// 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 } 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: ['./value-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() + aliasController: IAliasController; + + @Input() + targetDevice: TargetDevice; + + @Input() + popover: TbPopoverComponent; + + @Output() + getValueSettingsApplied = new EventEmitter>(); + + getValueAction = GetValueAction; + + getValueActions = getValueActions; + + 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.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]], + subscribeForUpdates: [this.getValueSettings?.getAttribute?.subscribeForUpdates, []] + }), + getTimeSeries: this.fb.group({ + key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]], + subscribeForUpdates: [this.getValueSettings?.getTimeSeries?.subscribeForUpdates, []] + }), + 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 61% 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..3c3b00531a 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,38 @@ 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'; @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: './value-action-settings-button.component.html', + styleUrls: ['./value-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() aliasController: IAliasController; @@ -67,7 +70,7 @@ export class RpcInitialStateSettingsComponent implements OnInit, ControlValueAcc @Input() disabled = false; - modelValue: RpcInitialStateSettings; + modelValue: GetValueSettings; displayValue: string; @@ -95,12 +98,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) { + openValueActionSettingsPopup($event: Event, matButton: MatButton) { if ($event) { $event.stopPropagation(); } @@ -109,21 +112,22 @@ 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, aliasController: this.aliasController, targetDevice: this.targetDevice }; - 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 +136,22 @@ 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 ? 'widgets.value-action.on' : 'widgets.value-action.off'); } 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/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 56% 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..faf27484f9 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-constant' | translate }} + {{ 'widgets.value-action.converter-function' | translate }} + {{ 'widgets.value-action.converter-none' | translate }}
- -
-
@@ -132,16 +132,16 @@
-
{{ 'widgets.rpc-state.request-timeout-ms' | translate }}
+
{{ 'widgets.value-action.request-timeout-ms' | translate }}
warning @@ -149,28 +149,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 +183,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..229d55c531 --- /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,182 @@ +/// +/// 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 { + getValueActions, + SetValueAction, + setValueActions, + setValueActionTranslations, + SetValueSettings, + ValueToDataType +} from '@shared/models/action-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-set-value-action-settings-panel', + templateUrl: './set-value-action-settings-panel.component.html', + providers: [], + styleUrls: ['./value-action-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SetValueActionSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + panelTitle: string; + + @Input() + setValueSettings: SetValueSettings; + + @Input() + valueType: ValueType; + + @Input() + aliasController: IAliasController; + + @Input() + targetDevice: TargetDevice; + + @Input() + popover: TbPopoverComponent; + + @Output() + setValueSettingsApplied = new EventEmitter(); + + setValueAction = SetValueAction; + + setValueActions = setValueActions; + + 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.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 62% 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..d0110226f5 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 { 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'; @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: './value-action-settings-button.component.html', + styleUrls: ['./value-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; @Input() aliasController: IAliasController; @@ -75,7 +71,7 @@ export class RpcUpdateStateSettingsComponent implements OnInit, ControlValueAcce @Input() disabled = false; - modelValue: RpcUpdateStateSettings; + modelValue: SetValueSettings; displayValue: string; @@ -103,12 +99,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) { + openValueActionSettingsPopup($event: Event, matButton: MatButton) { if ($event) { $event.stopPropagation(); } @@ -117,22 +113,22 @@ 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 }; - 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 +137,31 @@ 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.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/rpc/rpc-state-settings-button.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/value-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/value-action-settings-button.component.html index da24002e31..1e54dc3270 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/value-action-settings-button.component.html @@ -16,9 +16,9 @@ -->
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..d1442c09c5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts @@ -0,0 +1,128 @@ +/// +/// 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'; + +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 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 GetTelemetryValueSettings extends TelemetryValueSettings { + subscribeForUpdates: boolean; +} + +export interface GetAttributeValueSettings extends GetTelemetryValueSettings { + 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: GetTelemetryValueSettings; + 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 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 { + 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; +} + +/*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/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/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0f92abd1f3..e18d21f609 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6011,60 +6011,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", @@ -6537,6 +6483,62 @@ "source-entity-alias": "Source entity alias", "source-entity-attribute": "Source entity attribute" }, + "value-action": { + "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.", + "subscribe-for-updates": "Subscribe for updates", + "subscribe-for-updates-hint": "Subscribe for updates", + "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." + } + }, "widget-font": { "font-settings": "Font settings", "font-family": "Font family", From df91e0a38e302862c269de7afb276b7ff35f9ee4 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 13:57:39 +0200 Subject: [PATCH 054/128] Update the Bug report template --- .github/ISSUE_TEMPLATE/---bug-report.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index bffb88ebb7..07c5c150cb 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -2,8 +2,8 @@ name: "\U0001F41E Bug report" about: Create a report to help us improve title: "[Bug] " -labels: bug -assignees: ashvayka, vvlladd28 +labels: ['bug', 'status: 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. From 098f72b86b72764a9ef497eb8b347e24311e924e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:00:47 +0200 Subject: [PATCH 055/128] Update ---bug-report.md --- .github/ISSUE_TEMPLATE/---bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 07c5c150cb..33a0652e15 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -2,7 +2,7 @@ name: "\U0001F41E Bug report" about: Create a report to help us improve title: "[Bug] " -labels: ['bug', 'status: unconfirmed'] +labels: ['bug', 'unconfirmed'] assignees: AndriichnekoDm --- From f6b796027edbd3f9fda7af173f83d56143eb886d Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:01:58 +0200 Subject: [PATCH 056/128] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e5a710c155..fcb1448199 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: "[Feature Request]" -labels: ['Feature'] +labels: ['feature'] assignees: 'AndriichnekoDm' --- From 7dfc89aac22e186ac270665ed137d063af6e4f98 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:04:12 +0200 Subject: [PATCH 057/128] Update question.md --- .github/ISSUE_TEMPLATE/question.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 99d82ca698..3fb84817ca 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 +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** From 6d624ce17d2a2022b6cb6268120a6db3d23f06d3 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:04:54 +0200 Subject: [PATCH 058/128] Update question.md --- .github/ISSUE_TEMPLATE/question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 3fb84817ca..2f15690f68 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,6 +1,6 @@ --- name: Question -about: Describe your questions in details +about: Describe your questions in detail title: "Your title here" labels: ['question'] assignees: 'AndriichnekoDm' From 08c8a455a575fe2f7de551b160128dc27fd350d2 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:05:20 +0200 Subject: [PATCH 059/128] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index fcb1448199..c73f810cd8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,7 +1,7 @@ --- name: Feature request about: Suggest an idea for this project -title: "[Feature Request]" +title: "Your title here" labels: ['feature'] assignees: 'AndriichnekoDm' From 847bbbdd228efeeb0d3c08ac7e40bf1e50633c52 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Tue, 30 Jan 2024 14:05:42 +0200 Subject: [PATCH 060/128] Update ---bug-report.md --- .github/ISSUE_TEMPLATE/---bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 33a0652e15..6d8889f98d 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -1,7 +1,7 @@ --- name: "\U0001F41E Bug report" about: Create a report to help us improve -title: "[Bug] " +title: "Your title here" labels: ['bug', 'unconfirmed'] assignees: AndriichnekoDm From 8fe4947e1ec67dcef856ad4db4a8e9824081aab1 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 30 Jan 2024 14:42:07 +0200 Subject: [PATCH 061/128] Generic database error message; use TB StringUtils in BaseController --- .../server/controller/BaseController.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) 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 414d13a714..b8a787f56c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -18,10 +18,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.node.ObjectNode; 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; @@ -48,6 +45,7 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; @@ -171,7 +169,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; -import static org.apache.commons.lang3.StringUtils.isNotEmpty; +import static org.thingsboard.server.common.data.StringUtils.isNotEmpty; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -378,15 +376,11 @@ 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) { - String sqlError = Optional.ofNullable(((PSQLException) rootCause).getServerErrorMessage()) - .map(ServerErrorMessage::getMessage).orElse(null); - if (isNotEmpty(sqlError)) { - sqlError = StringUtils.capitalize(sqlError); - return new ThingsboardException(sqlError, ThingsboardErrorCode.GENERAL); - } + String errorType = exception.getClass().getSimpleName(); + if (!logControllerErrorStackTrace) { // not to log the error twice + log.warn("Database error: {} - {}", errorType, ExceptionUtils.getRootCauseMessage(exception)); } + return new ThingsboardException("Database error: " + errorType, ThingsboardErrorCode.GENERAL); } return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL); } From 5875a401184874888423ba22f61ce8d840467223 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 30 Jan 2024 15:16:53 +0200 Subject: [PATCH 062/128] Error handling for PartitionChangeEvent listeners --- .../DefaultSubscriptionManagerService.java | 8 ++++---- .../queue/discovery/HashPartitionService.java | 14 ++++++++++++-- .../discovery/TbApplicationEventListener.java | 6 +++++- .../vc/DefaultClusterVersionControlService.java | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) 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 b04291a303..c073aa6a95 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/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 bfc50e076b..de3c394c22 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 @@ -251,7 +251,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) { @@ -376,7 +381,12 @@ public class HashPartitionService implements PartitionService { .collect(Collectors.toList())) .collect(Collectors.joining(System.lineSeparator()))); } - applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceType, partitionsMap)); + PartitionChangeEvent event = new PartitionChangeEvent(this, serviceType, partitionsMap); + try { + applicationEventPublisher.publishEvent(event); + } catch (Exception e) { + log.error("Failed to publish partition change event {}", event, e); + } } @Override 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 8e805aac3d..341ab0f1eb 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/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 fa45ddf98c..1d78b5ddd1 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 { From ce9351ba276f40f5e9f080519def16239621ec0d Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 30 Jan 2024 18:20:23 +0200 Subject: [PATCH 063/128] deleted unused classes --- ...leEngineQueueAckStrategyConfiguration.java | 30 ----------------- .../TbRuleEngineQueueConfiguration.java | 33 ------------------- ...ngineQueueSubmitStrategyConfiguration.java | 27 --------------- 3 files changed, 90 deletions(-) delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java delete mode 100644 common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java deleted file mode 100644 index 8edb224fec..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java +++ /dev/null @@ -1,30 +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.queue.settings; - -import lombok.Data; - -@Data -@Deprecated -public class TbRuleEngineQueueAckStrategyConfiguration { - - private String type; - private int retries; - private double failurePercentage; - private long pauseBetweenRetries; - private long maxPauseBetweenRetries; - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java deleted file mode 100644 index 8199977618..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueConfiguration.java +++ /dev/null @@ -1,33 +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.queue.settings; - -import lombok.Data; - -@Data -@Deprecated -public class TbRuleEngineQueueConfiguration { - - private String name; - private String topic; - private int pollInterval; - private int partitions; - private boolean consumerPerPartition; - private long packProcessingTimeout; - private TbRuleEngineQueueSubmitStrategyConfiguration submitStrategy; - private TbRuleEngineQueueAckStrategyConfiguration processingStrategy; - -} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java deleted file mode 100644 index f073afad17..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java +++ /dev/null @@ -1,27 +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.queue.settings; - -import lombok.Data; - -@Data -@Deprecated -public class TbRuleEngineQueueSubmitStrategyConfiguration { - - private String type; - private int batchSize; - -} From 2d7c1a3cd521fca2187fe3e503c5a5f6c3f7194a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 31 Jan 2024 12:15:06 +0200 Subject: [PATCH 064/128] Implement JpaPartitionedAbstractDao --- .../server/controller/BaseController.java | 2 +- .../server/dao/sql/JpaAbstractDao.java | 24 +++-------- .../dao/sql/JpaPartitionedAbstractDao.java | 43 +++++++++++++++++++ .../dao/sql/alarm/JpaAlarmCommentDao.java | 11 ++--- .../server/dao/sql/audit/JpaAuditLogDao.java | 9 +--- .../dao/sql/edge/JpaBaseEdgeEventDao.java | 9 +--- .../sql/notification/JpaNotificationDao.java | 9 +--- 7 files changed, 58 insertions(+), 49 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java 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 b8a787f56c..6851029d1e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -380,7 +380,7 @@ public abstract class BaseController { if (!logControllerErrorStackTrace) { // not to log the error twice log.warn("Database error: {} - {}", errorType, ExceptionUtils.getRootCauseMessage(exception)); } - return new ThingsboardException("Database error: " + errorType, ThingsboardErrorCode.GENERAL); + return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL); } return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL); } 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 92bc4187c1..fe0567b8d8 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 @@ -43,9 +43,6 @@ public abstract class JpaAbstractDao, D> extends JpaAbstractDaoListeningExecutorService implements Dao { - @PersistenceContext - private EntityManager entityManager; - protected abstract Class getEntityClass(); protected abstract JpaRepository getRepository(); @@ -67,18 +64,14 @@ public abstract class JpaAbstractDao, D> entity.setUuid(uuid); entity.setCreatedTime(Uuids.unixTimestamp(uuid)); } - - if (isPartitioned()) { - createPartition(entity); - } - if (isNew) { - entityManager.persist(entity); - } else { - entity = entityManager.merge(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) { @@ -132,11 +125,4 @@ public abstract class JpaAbstractDao, D> return DaoUtil.convertDataList(entities); } - public boolean isPartitioned() { - return false; - } - - public void createPartition(E entity) { - } - } 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..76db367ffb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 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 47df3bd0c6..1b1dc825ce 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 @@ -31,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; @@ -44,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; @@ -53,7 +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))); @@ -71,11 +71,6 @@ public class JpaAlarmCommentDao extends JpaAbstractDao implements AuditLogDao { +public class JpaAuditLogDao extends JpaPartitionedAbstractDao implements AuditLogDao { private final AuditLogRepository auditLogRepository; private final SqlPartitioningRepository partitioningRepository; @@ -158,11 +158,6 @@ public class JpaAuditLogDao extends JpaAbstractDao imp jdbcTemplate.update("CALL migrate_audit_logs(?, ?, ?)", startTime, endTime, partitionSizeInMs); } - @Override - public boolean isPartitioned() { - return true; - } - @Override 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 1f8d0c8026..d37e28bd0f 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; @@ -57,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; @@ -228,11 +228,6 @@ public class JpaBaseEdgeEventDao extends JpaAbstractDao implements NotificationDao { +public class JpaNotificationDao extends JpaPartitionedAbstractDao implements NotificationDao { private final NotificationRepository notificationRepository; private final SqlPartitioningRepository partitioningRepository; @@ -100,11 +100,6 @@ public class JpaNotificationDao extends JpaAbstractDao Date: Wed, 31 Jan 2024 14:37:16 +0200 Subject: [PATCH 065/128] fixed license --- .../thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 76db367ffb..a933837f5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2023 The Thingsboard Authors + * 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. From 54e9aba8fb2cb36de40fc260b9926a54fc50db00 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Wed, 31 Jan 2024 16:59:39 +0200 Subject: [PATCH 066/128] Fix compilation issue --- .../service/edge/rpc/processor/alarm/BaseAlarmProcessor.java | 2 +- .../thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java index 76db367ffb..a933837f5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -1,5 +1,5 @@ /** - * Copyright © 2016-2023 The Thingsboard Authors + * 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. From 6614928bce91aa2afd15ea942b97cc82ea59128b Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Wed, 31 Jan 2024 17:06:49 +0200 Subject: [PATCH 067/128] UI: improvements for form scss --- ...uid-level-card-basic-config.component.html | 30 +++++++++---------- ...-level-card-widget-settings.component.html | 24 +++++++-------- ui-ngx/src/form.scss | 23 ++++++++++++++ 3 files changed, 50 insertions(+), 27 deletions(-) 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 4940df18ba..2653842e6d 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
-
+
@@ -186,9 +186,9 @@
-
+
widgets.liquid-level-card.total-volume-units
-
+
@@ -226,7 +226,7 @@
widgets.liquid-level-card.value
-
+
@@ -241,7 +241,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -267,7 +267,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -288,7 +288,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.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index d549198273..6e3967c67d 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,7 +19,7 @@
-
+
widgets.liquid-level-card.shape
@@ -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
-
+
@@ -143,9 +143,9 @@
-
+
widgets.liquid-level-card.total-volume-units
-
+
@@ -188,7 +188,7 @@
widgets.liquid-level-card.value
-
+
@@ -198,7 +198,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -232,7 +232,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -252,7 +252,7 @@ {{ 'widgets.value-card.date' | translate }} -
+
diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index ee6d4167cd..8b857c05c8 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -211,6 +211,29 @@ } } + .tb-flex-centered-row { + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + gap: 8px; + &.flex-start { + justify-content: flex-start; + } + &.flex-end { + justify-content: flex-end; + } + &.space-between { + justify-content: space-between; + } + &.no-gap { + gap: 0; + } + &.fill-width { + width: 100%; + } + } + .tb-form-panel, .tb-form-row { .mat-slide { margin: 0; From ac04e6c7ba6cc041f8f586dc1428fc603750fe65 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Wed, 31 Jan 2024 17:17:17 +0200 Subject: [PATCH 068/128] refactoring for tb-flex-row --- ...uid-level-card-basic-config.component.html | 24 +++++++++---------- ...-level-card-widget-settings.component.html | 20 ++++++++-------- ui-ngx/src/form.scss | 6 +++-- 3 files changed, 26 insertions(+), 24 deletions(-) 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 2653842e6d..fd568ad021 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
-
+
@@ -132,7 +132,7 @@
widgets.liquid-level-card.widget-units
-
+
@@ -156,7 +156,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -188,7 +188,7 @@
widgets.liquid-level-card.total-volume-units
-
+
@@ -226,7 +226,7 @@
widgets.liquid-level-card.value
-
+
@@ -241,7 +241,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -267,7 +267,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -288,7 +288,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.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index 6e3967c67d..8c5e761ea6 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
-
+
@@ -113,7 +113,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -145,7 +145,7 @@
widgets.liquid-level-card.total-volume-units
-
+
@@ -188,7 +188,7 @@
widgets.liquid-level-card.value
-
+
@@ -198,7 +198,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -232,7 +232,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -252,7 +252,7 @@ {{ 'widgets.value-card.date' | translate }} -
+
diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 8b857c05c8..b88a11979a 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -211,11 +211,10 @@ } } - .tb-flex-centered-row { + .tb-flex-row { display: flex; flex: 1; flex-direction: row; - align-items: center; gap: 8px; &.flex-start { justify-content: flex-start; @@ -226,6 +225,9 @@ &.space-between { justify-content: space-between; } + &.align-center { + align-items: center; + } &.no-gap { gap: 0; } From b298f5a52a327975665f3c4c61349f3488f69301 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Wed, 31 Jan 2024 17:43:57 +0200 Subject: [PATCH 069/128] refactoring for tb-flex --- ...uid-level-card-basic-config.component.html | 24 +++++++++---------- ...-level-card-widget-settings.component.html | 18 +++++++------- ui-ngx/src/form.scss | 9 +++++-- 3 files changed, 28 insertions(+), 23 deletions(-) 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 fd568ad021..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
-
+
@@ -132,7 +132,7 @@
widgets.liquid-level-card.widget-units
-
+
@@ -156,7 +156,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -188,7 +188,7 @@
widgets.liquid-level-card.total-volume-units
-
+
@@ -226,7 +226,7 @@
widgets.liquid-level-card.value
-
+
@@ -241,7 +241,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -267,7 +267,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -288,7 +288,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.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index 8c5e761ea6..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,7 +19,7 @@
-
+
widgets.liquid-level-card.shape
@@ -51,7 +51,7 @@ formControlName="shapeAttributeName">
-
+
widgets.liquid-level-card.shape-by-attribute
widgets.liquid-level-card.widget-units
-
+
@@ -113,7 +113,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -145,7 +145,7 @@
widgets.liquid-level-card.total-volume-units
-
+
@@ -188,7 +188,7 @@
widgets.liquid-level-card.value
-
+
@@ -198,7 +198,7 @@
widgets.liquid-level-card.total-volume
-
+
@@ -232,7 +232,7 @@ {{ 'widgets.liquid-level-card.level' | translate }} -
+
@@ -252,7 +252,7 @@ {{ 'widgets.value-card.date' | translate }} -
+
diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index b88a11979a..eb26a95455 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -211,11 +211,16 @@ } } - .tb-flex-row { + .tb-flex { display: flex; flex: 1; - flex-direction: row; gap: 8px; + &.row { + flex-direction: row; + } + &.column { + flex-direction: column; + } &.flex-start { justify-content: flex-start; } From 94bca91b7b2fd13ec222a283b97094801296d590 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Wed, 31 Jan 2024 18:33:41 +0200 Subject: [PATCH 070/128] UI: added queueName to rule-node copy buffer --- ui-ngx/src/app/core/services/item-buffer.service.ts | 3 +++ 1 file changed, 3 insertions(+) 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 b928245765..e1941590ea 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -312,6 +312,9 @@ export class ItemBufferService { 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; From 959704f16d9df26dcd5239bbb999f99bab8f1dc6 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 1 Feb 2024 12:12:02 +0200 Subject: [PATCH 071/128] UI: added improvements and refactoring for snmp --- ...ce-profile-communication-config.component.scss | 15 ++++++++------- .../snmp-device-profile-mapping.component.html | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) 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 870b627378..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,19 +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: row; - box-sizing: border-box; + flex-direction: column; display: flex; place-content: stretch flex-start; align-items: stretch; - flex: 1 1 0; - gap: 8px; + flex: 1; + gap: 0; } .scope-row { @@ -36,10 +37,10 @@ margin: 16px 0 } - @media (max-width: 1650px) { + @media #{$mat-gt-xmd} { .communication-config { - flex-direction: column; - gap: 0; + flex-direction: row; + gap: 8px; } } } 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 94330f7c23..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 @@
- - + +
From 9b32a51d4b100d26f976b52e73a83ec380f18722 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 1 Feb 2024 13:46:31 +0200 Subject: [PATCH 072/128] Refactoring due to review. Fix naming, improve upgrade logic --- .../server/service/edge/rpc/EdgeGrpcSession.java | 4 +++- .../install/DefaultSystemDataLoaderService.java | 3 +++ .../notification/DefaultNotificationCenter.java | 10 ++++++---- .../EdgeCommunicationFailureTriggerProcessor.java | 2 +- .../impl/NotificationRuleExportService.java | 7 ++++--- .../EdgeCommunicationFailureNotificationInfo.java | 1 - .../info/EdgeConnectionNotificationInfo.java | 1 - .../trigger/EdgeCommunicationFailureTrigger.java | 2 +- .../rule/trigger/EdgeConnectionTrigger.java | 4 ++-- .../server/dao/edge/BaseEdgeEventService.java | 7 ++++--- .../DefaultNotificationSettingsService.java | 13 ++++++++----- .../notification/edge_communication_failure.md | 2 +- .../help/en_US/notification/edge_connection.md | 2 +- 13 files changed, 34 insertions(+), 24 deletions(-) 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 44bdfcdb1c..b61d7d5e95 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 @@ -479,11 +479,13 @@ public final class EdgeGrpcSession implements Closeable { 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) { + 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(message).build()); + .edgeId(edge.getId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(message).error(error).build()); sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId()); } else { sendDownlinkMsg(ResponseMsg.newBuilder() 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 fcc829886c..e50da5ea62 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 @@ -696,6 +696,7 @@ 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)); @@ -710,6 +711,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { } }); } + 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/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 5bbde6de81..be37d98bb5 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 @@ -241,11 +241,13 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple case PLATFORM_USERS: { PlatformUsersNotificationTargetConfig targetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration(); if (targetConfig.getUsersFilter().getType().isForRules() && ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) { - recipients = new PageDataIterable<>(pageLink -> - notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink), 500); + recipients = new PageDataIterable<>(pageLink -> { + return notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink); + }, 500); } else { - recipients = new PageDataIterable<>(pageLink -> - notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink), 500); + recipients = new PageDataIterable<>(pageLink -> { + return notificationTargetService.findRecipientsForNotificationTargetConfig(ctx.getTenantId(), targetConfig, pageLink); + }, 500); } break; } 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 index dc54024eec..38d8ae9805 100644 --- 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 @@ -53,7 +53,7 @@ public class EdgeCommunicationFailureTriggerProcessor implements NotificationRul } private String truncateFailureMsg(String input) { - int maxLength = 800; + int maxLength = 500; if (input != null && input.length() > maxLength) { return input.substring(0, maxLength); } 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 40d7d7c2b5..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,8 +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.EdgeConnectionNotificationRuleTriggerConfig; 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; @@ -92,8 +92,9 @@ public class NotificationRuleExportService> escalationTable = new LinkedHashMap<>(recipientsConfig.getEscalationTable()); - escalationTable.replaceAll((delay, targets) -> - toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList())); + escalationTable.replaceAll((delay, targets) -> { + return toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList()); + }); recipientsConfig.setEscalationTable(escalationTable); break; } 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 index 2edab592d5..6d0f2ae5f5 100644 --- 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 @@ -43,7 +43,6 @@ public class EdgeCommunicationFailureNotificationInfo implements RuleOriginatedN @Override public Map getTemplateData() { return mapOf( - "tenantId", tenantId.toString(), "edgeId", edgeId.toString(), "edgeName", edgeName, "failureMsg", failureMsg 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 index e7b6494fdb..62b1370566 100644 --- 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 @@ -44,7 +44,6 @@ public class EdgeConnectionNotificationInfo implements RuleOriginatedNotificatio public Map getTemplateData() { return mapOf( "eventType", eventType, - "tenantId", tenantId.toString(), "edgeId", edgeId.toString(), "edgeName", edgeName ); 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 index 6400a5c67a..6212c22a0d 100644 --- 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 @@ -43,7 +43,7 @@ public class EdgeCommunicationFailureTrigger implements NotificationRuleTrigger @Override public String getDeduplicationKey() { - return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), edgeId.toString(), error); + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), error); } @Override 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 index fe5f6fa6b1..766338dba6 100644 --- 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 @@ -42,12 +42,12 @@ public class EdgeConnectionTrigger implements NotificationRuleTrigger { @Override public String getDeduplicationKey() { - return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), edgeName, String.valueOf(connected)); + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), String.valueOf(connected)); } @Override public long getDefaultDeduplicationDuration() { - return TimeUnit.MINUTES.toMillis(30); + return TimeUnit.MINUTES.toMillis(1); } @Override 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 6a05b98d16..43c3dc4914 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 @@ -41,9 +41,10 @@ public class BaseEdgeEventService implements EdgeEventService { @Override public ListenableFuture saveAsync(EdgeEvent edgeEvent) { - boolean isEdgeEventTenantRateLimitReached = !rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS, edgeEvent.getTenantId()); - boolean isEdgeEventRateLimitPerEdgeReached = !rateLimitService.checkRateLimit(LimitedApi.EDGE_EVENTS_PER_EDGE, edgeEvent.getTenantId(), edgeEvent.getEdgeId()); - if (isEdgeEventTenantRateLimitReached || isEdgeEventRateLimitPerEdgeReached) { + 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); 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 b42fdd7645..c858eecf62 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 @@ -210,19 +210,22 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); } else { - List requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE); - List existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes( + 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.toList()); + .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(), - tenantId.isSysTenantId() ? "All tenant administrators" : "Tenant administrators")); + .orElseGet(() -> createTarget(tenantId, "Tenant administrators", new TenantAdministratorsFilter(), "Tenant administrators")); for (NotificationType type : requiredNotificationTypes) { if (!existingNotificationTypes.contains(type)) { 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 index 03f2a2816a..712f2b45e7 100644 --- 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 @@ -1,4 +1,4 @@ -#### Edge notification templatization +#### Edge communication failure notification templatization

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 index fed6c0372e..37f0ec7573 100644 --- a/ui-ngx/src/assets/help/en_US/notification/edge_connection.md +++ b/ui-ngx/src/assets/help/en_US/notification/edge_connection.md @@ -1,4 +1,4 @@ -#### Edge notification templatization +#### Edge connection notification templatization

From 61ce68854deacbf71e6aaff1a33dcb84471e8c85 Mon Sep 17 00:00:00 2001 From: rusikv Date: Thu, 1 Feb 2024 13:59:08 +0200 Subject: [PATCH 073/128] UI: fixed chart card value color differs from other widgets with same values and range colors configs --- .../widget/lib/cards/aggregated-value-card-widget.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..dc3b095552 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 @@ -242,7 +242,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit aggValue.value = 'N/A'; } const numeric = formatNumberValue(value, (aggValue.key.decimals || this.ctx.decimals)); - aggValue.color.update(numeric); + aggValue.color.update(value); if (aggValue.showArrow && isDefined(numeric)) { aggValue.upArrow = numeric > 0; aggValue.downArrow = numeric < 0; From 4b29a8f3ff3aec1777828630e03e7ea20841ba0f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 1 Feb 2024 19:16:37 +0200 Subject: [PATCH 074/128] UI: Implement action button widget. --- .../json/system/widget_bundles/buttons.json | 13 + .../system/widget_types/action_button.json | 27 ++ ui-ngx/src/app/core/api/widget-api.models.ts | 1 + ui-ngx/src/app/core/services/utils.service.ts | 26 +- ui-ngx/src/app/modules/common/modules-map.ts | 16 +- .../dashboard/dashboard.component.html | 1 + .../dashboard/dashboard.component.ts | 3 + .../action/widget-action-dialog.component.ts | 11 +- .../basic/basic-widget-config.module.ts | 12 +- .../action-button-basic-config.component.html | 77 ++++++ .../action-button-basic-config.component.ts | 139 ++++++++++ .../single-switch-basic-config.component.html | 18 +- .../widget/config/datasource.component.html | 4 +- .../widget/config/datasource.component.ts | 30 +- .../widget/config/datasources.component.ts | 5 +- .../config/widget-config-components.module.ts | 17 +- .../config/widget-config.component.models.ts | 14 +- .../widget/lib/action/action-widget.scss | 46 ++++ .../action-button-widget.component.html | 35 +++ .../action-button-widget.component.scss | 28 ++ .../button/action-button-widget.component.ts | 100 +++++++ .../lib/button/action-button-widget.models.ts | 67 +++++ .../rpc/single-switch-widget.component.html | 8 +- .../rpc/single-switch-widget.component.scss | 30 -- .../lib/rpc/single-switch-widget.component.ts | 26 +- ... => action-settings-button.component.html} | 4 +- ...utton.scss => action-settings-button.scss} | 2 +- ...s => action-settings-panel.component.scss} | 8 +- ...custom-action-pretty-editor.component.html | 0 ...custom-action-pretty-editor.component.scss | 2 +- .../custom-action-pretty-editor.component.ts | 4 +- ...ction-pretty-resources-tabs.component.html | 0 ...ction-pretty-resources-tabs.component.scss | 0 ...-action-pretty-resources-tabs.component.ts | 2 +- .../common}/action/custom-action.models.ts | 0 .../common}/action/custom-sample-css.raw | 0 .../common}/action/custom-sample-html.raw | 0 .../common}/action/custom-sample-js.raw | 0 ...value-action-settings-panel.component.html | 14 +- ...t-value-action-settings-panel.component.ts | 21 +- .../get-value-action-settings.component.ts | 29 +- .../mobile-action-editor.component.html | 0 .../action/mobile-action-editor.component.ts | 4 +- .../action/mobile-action-editor.models.ts | 0 ...value-action-settings-panel.component.html | 8 +- ...t-value-action-settings-panel.component.ts | 12 +- .../set-value-action-settings.component.ts | 14 +- ...idget-action-settings-panel.component.html | 42 +++ .../widget-action-settings-panel.component.ts | 88 ++++++ .../widget-action-settings.component.ts | 136 +++++++++ .../action/widget-action.component.html | 0 .../common}/action/widget-action.component.ts | 2 +- .../widget-button-appearance.component.html | 97 +++++++ .../widget-button-appearance.component.ts | 141 ++++++++++ ...t-button-custom-style-panel.component.html | 93 +++++++ ...t-button-custom-style-panel.component.scss | 68 +++++ ...get-button-custom-style-panel.component.ts | 171 ++++++++++++ .../widget-button-custom-style.component.html | 44 +++ .../widget-button-custom-style.component.scss | 46 ++++ .../widget-button-custom-style.component.ts | 157 +++++++++++ .../common/widget-settings-common.module.ts | 47 +++- ...ngle-switch-widget-settings.component.html | 18 +- ...single-switch-widget-settings.component.ts | 6 +- .../widget/widget-component.service.ts | 3 + .../widget/widget-components.module.ts | 7 +- .../widget/widget-container.component.html | 2 + .../widget/widget-container.component.scss | 7 + .../widget/widget-container.component.ts | 24 +- .../widget/widget-preview.component.html | 1 + .../components/widget/widget.component.ts | 16 +- .../home/models/widget-component.models.ts | 2 + .../button/widget-button.component.html | 39 +++ .../button/widget-button.component.scss | 189 +++++++++++++ .../button/widget-button.component.ts | 178 ++++++++++++ .../components/button/widget-button.models.ts | 259 ++++++++++++++++++ .../models/action-widget-settings.models.ts | 19 +- .../shared/models/widget-settings.models.ts | 27 +- ui-ngx/src/app/shared/models/widget.models.ts | 26 ++ ui-ngx/src/app/shared/shared.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 47 +++- ui-ngx/src/assets/widget/button/basic.svg | 17 ++ ui-ngx/src/assets/widget/button/filled.svg | 19 ++ ui-ngx/src/assets/widget/button/outlined.svg | 20 ++ .../src/assets/widget/button/underlined.svg | 23 ++ ui-ngx/src/form.scss | 8 + 85 files changed, 2790 insertions(+), 184 deletions(-) create mode 100644 application/src/main/data/json/system/widget_bundles/buttons.json create mode 100644 application/src/main/data/json/system/widget_types/action_button.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/action-button-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/action-button-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts rename ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/{value-action-settings-button.component.html => action-settings-button.component.html} (88%) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/{value-action-settings-button.scss => action-settings-button.scss} (95%) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/{value-action-settings-panel.component.scss => action-settings-panel.component.scss} (87%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-editor.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-editor.component.scss (95%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-editor.component.ts (95%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-resources-tabs.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-resources-tabs.component.scss (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action-pretty-resources-tabs.component.ts (99%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-action.models.ts (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-sample-css.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-sample-html.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/custom-sample-js.raw (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/mobile-action-editor.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/mobile-action-editor.component.ts (98%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/mobile-action-editor.models.ts (100%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings-panel.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action-settings.component.ts rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/widget-action.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/{config => lib/settings/common}/action/widget-action.component.ts (99%) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-appearance.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts create mode 100644 ui-ngx/src/app/shared/components/button/widget-button.component.html create mode 100644 ui-ngx/src/app/shared/components/button/widget-button.component.scss create mode 100644 ui-ngx/src/app/shared/components/button/widget-button.component.ts create mode 100644 ui-ngx/src/app/shared/components/button/widget-button.models.ts create mode 100644 ui-ngx/src/assets/widget/button/basic.svg create mode 100644 ui-ngx/src/assets/widget/button/filled.svg create mode 100644 ui-ngx/src/assets/widget/button/outlined.svg create mode 100644 ui-ngx/src/assets/widget/button/underlined.svg 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..046e16dd7e --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -0,0 +1,13 @@ +{ + "widgetsBundle": { + "alias": "buttons", + "title": "Buttons", + "image": null, + "description": null, + "order": 7500, + "name": "Buttons" + }, + "widgetTypeFqns": [ + "action_button" + ] +} \ No newline at end of file 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..3ad518ce23 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/action_button.json @@ -0,0 +1,27 @@ +{ + "fqn": "action_button", + "name": "Action button", + "deprecated": false, + "image": "tb-image:YWN0aW9uLWJ1dHRvbi5zdmc=:IkFjdGlvbiBidXR0b24iIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NzMgODkuMzMzM1Y4Mi4zMzMzSDY2LjgzNFY4OS4zMzMzSDcyLjY2NzNWODBINzYuMTY3M0w2NC41MDA3IDY5LjVMNTIuODM0IDgwSDU2LjMzNFY4OS4zMzMzSDYyLjE2NzNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDgwLjkzMTZIODYuMjFMODYuMTg4NSA3OC45NjU4SDg5LjY2ODlDOTAuMjU2MiA3OC45NjU4IDkwLjc1MzkgNzguODc5OSA5MS4xNjIxIDc4LjcwOEM5MS41Nzc1IDc4LjUyOSA5MS44OTI2IDc4LjI3NDcgOTIuMTA3NCA3Ny45NDUzQzkyLjMyMjMgNzcuNjA4NyA5Mi40Mjk3IDc3LjIwNDEgOTIuNDI5NyA3Ni43MzE0QzkyLjQyOTcgNzYuMjA4NyA5Mi4zMjk0IDc1Ljc4MjYgOTIuMTI4OSA3NS40NTMxQzkxLjkyODQgNzUuMTIzNyA5MS42MjA0IDc0Ljg4MzggOTEuMjA1MSA3NC43MzM0QzkwLjc5NjkgNzQuNTgzIDkwLjI3NDEgNzQuNTA3OCA4OS42MzY3IDc0LjUwNzhIODcuMDI2NFY4OEg4NC4zMzAxVjcyLjM1OTRIODkuNjM2N0M5MC40OTYxIDcyLjM1OTQgOTEuMjYyNCA3Mi40NDE3IDkxLjkzNTUgNzIuNjA2NEM5Mi42MTU5IDcyLjc3MTIgOTMuMTkyNCA3My4wMjkgOTMuNjY1IDczLjM3OTlDOTQuMTQ0OSA3My43MjM2IDk0LjUwNjUgNzQuMTYwNSA5NC43NSA3NC42OTA0Qzk1LjAwMDcgNzUuMjIwNCA5NS4xMjYgNzUuODUwNiA5NS4xMjYgNzYuNTgxMUM5NS4xMjYgNzcuMjI1NiA5NC45NzIgNzcuODE2NCA5NC42NjQxIDc4LjM1MzVDOTQuMzU2MSA3OC44ODM1IDkzLjkwMTQgNzkuMzE2NyA5My4yOTk4IDc5LjY1MzNDOTIuNjk4MiA3OS45ODk5IDkxLjk0OTkgODAuMTkwNCA5MS4wNTQ3IDgwLjI1NDlMOTAuMTk1MyA4MC45MzE2Wk05MC4wNzcxIDg4SDg1LjM2MTNMODYuNTc1MiA4NS44NjIzSDkwLjA3NzFDOTAuNjg1OSA4NS44NjIzIDkxLjE5NDMgODUuNzYyIDkxLjYwMjUgODUuNTYxNUM5Mi4wMTA3IDg1LjM1MzggOTIuMzE1MSA4NS4wNzEgOTIuNTE1NiA4NC43MTI5QzkyLjcyMzMgODQuMzQ3NyA5Mi44MjcxIDgzLjkyMTUgOTIuODI3MSA4My40MzQ2QzkyLjgyNzEgODIuOTI2MSA5Mi43Mzc2IDgyLjQ4NTcgOTIuNTU4NiA4Mi4xMTMzQzkyLjM3OTYgODEuNzMzNyA5Mi4wOTY3IDgxLjQ0MzcgOTEuNzEgODEuMjQzMkM5MS4zMjMyIDgxLjAzNTUgOTAuODE4NCA4MC45MzE2IDkwLjE5NTMgODAuOTMxNkg4Ny4xNjZMODcuMTg3NSA3OC45NjU4SDkxLjEyOTlMOTEuNzQyMiA3OS43MDdDOTIuNjAxNiA3OS43MzU3IDkzLjMwNyA3OS45MjU1IDkzLjg1ODQgODAuMjc2NEM5NC40MTcgODAuNjI3MyA5NC44MzI0IDgxLjA4MiA5NS4xMDQ1IDgxLjY0MDZDOTUuMzc2NiA4Mi4xOTkyIDk1LjUxMjcgODIuODAwOCA5NS41MTI3IDgzLjQ0NTNDOTUuNTEyNyA4NC40NDA4IDk1LjI5NDMgODUuMjc1MSA5NC44NTc0IDg1Ljk0ODJDOTQuNDI3NyA4Ni42MjE0IDkzLjgwODMgODcuMTMzNSA5Mi45OTkgODcuNDg0NEM5Mi4xODk4IDg3LjgyODEgOTEuMjE1OCA4OCA5MC4wNzcxIDg4Wk0xMDUuMjE2IDg1LjI2MDdWNzYuMzc3SDEwNy44MTVWODhIMTA1LjM2NkwxMDUuMjE2IDg1LjI2MDdaTTEwNS41ODEgODIuODQzOEwxMDYuNDUxIDgyLjgyMjNDMTA2LjQ1MSA4My42MDI5IDEwNi4zNjUgODQuMzIyNiAxMDYuMTkzIDg0Ljk4MTRDMTA2LjAyMSA4NS42MzMxIDEwNS43NTcgODYuMjAyNSAxMDUuMzk4IDg2LjY4OTVDMTA1LjA0IDg3LjE2OTMgMTA0LjU4MiA4Ny41NDUyIDEwNC4wMjMgODcuODE3NEMxMDMuNDY1IDg4LjA4MjQgMTAyLjc5NSA4OC4yMTQ4IDEwMi4wMTUgODguMjE0OEMxMDEuNDQ5IDg4LjIxNDggMTAwLjkzIDg4LjEzMjUgMTAwLjQ1NyA4Ny45Njc4Qzk5Ljk4NDQgODcuODAzMSA5OS41NzYyIDg3LjU0ODggOTkuMjMyNCA4Ny4yMDUxQzk4Ljg5NTggODYuODYxMyA5OC42MzQ0IDg2LjQxMzcgOTguNDQ4MiA4NS44NjIzQzk4LjI2MiA4NS4zMTA5IDk4LjE2ODkgODQuNjUyIDk4LjE2ODkgODMuODg1N1Y3Ni4zNzdIMTAwLjc1OFY4My45MDcyQzEwMC43NTggODQuMzI5OCAxMDAuODA4IDg0LjY4NDIgMTAwLjkwOCA4NC45NzA3QzEwMS4wMDggODUuMjUgMTAxLjE0NSA4NS40NzU2IDEwMS4zMTYgODUuNjQ3NUMxMDEuNDg4IDg1LjgxOTMgMTAxLjY4OSA4NS45NDExIDEwMS45MTggODYuMDEyN0MxMDIuMTQ3IDg2LjA4NDMgMTAyLjM5MSA4Ni4xMjAxIDEwMi42NDggODYuMTIwMUMxMDMuMzg2IDg2LjEyMDEgMTAzLjk2NiA4NS45NzY5IDEwNC4zODkgODUuNjkwNEMxMDQuODE4IDg1LjM5NjggMTA1LjEyMyA4NS4wMDI5IDEwNS4zMDIgODQuNTA4OEMxMDUuNDg4IDg0LjAxNDYgMTA1LjU4MSA4My40NTk2IDEwNS41ODEgODIuODQzOFpNMTE2LjA0NyA3Ni4zNzdWNzguMjY3NkgxMDkuNDk0Vjc2LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDczLjUzMDNIMTEzLjk3NFY4NC43ODgxQzExMy45NzQgODUuMTQ2MiAxMTQuMDI0IDg1LjQyMTkgMTE0LjEyNCA4NS42MTUyQzExNC4yMzEgODUuODAxNCAxMTQuMzc4IDg1LjkyNjggMTE0LjU2NCA4NS45OTEyQzExNC43NTEgODYuMDU1NyAxMTQuOTY5IDg2LjA4NzkgMTE1LjIyIDg2LjA4NzlDMTE1LjM5OSA4Ni4wODc5IDExNS41NzEgODYuMDc3MSAxMTUuNzM1IDg2LjA1NTdDMTE1LjkgODYuMDM0MiAxMTYuMDMzIDg2LjAxMjcgMTE2LjEzMyA4NS45OTEyTDExNi4xNDQgODcuOTY3OEMxMTUuOTI5IDg4LjAzMjIgMTE1LjY3OCA4OC4wODk1IDExNS4zOTIgODguMTM5NkMxMTUuMTEyIDg4LjE4OTggMTE0Ljc5IDg4LjIxNDggMTE0LjQyNSA4OC4yMTQ4QzExMy44MyA4OC4yMTQ4IDExMy4zMDQgODguMTExIDExMi44NDYgODcuOTAzM0MxMTIuMzg3IDg3LjY4ODUgMTEyLjAyOSA4Ny4zNDExIDExMS43NzEgODYuODYxM0MxMTEuNTE0IDg2LjM4MTUgMTExLjM4NSA4NS43NDQxIDExMS4zODUgODQuOTQ5MlY3My41MzAzWk0xMjMuNjIzIDc2LjM3N1Y3OC4yNjc2SDExNy4wN1Y3Ni4zNzdIMTIzLjYyM1pNMTE4Ljk2MSA3My41MzAzSDEyMS41NVY4NC43ODgxQzEyMS41NSA4NS4xNDYyIDEyMS42IDg1LjQyMTkgMTIxLjcgODUuNjE1MkMxMjEuODA4IDg1LjgwMTQgMTIxLjk1NCA4NS45MjY4IDEyMi4xNDEgODUuOTkxMkMxMjIuMzI3IDg2LjA1NTcgMTIyLjU0NSA4Ni4wODc5IDEyMi43OTYgODYuMDg3OUMxMjIuOTc1IDg2LjA4NzkgMTIzLjE0NyA4Ni4wNzcxIDEyMy4zMTIgODYuMDU1N0MxMjMuNDc2IDg2LjAzNDIgMTIzLjYwOSA4Ni4wMTI3IDEyMy43MDkgODUuOTkxMkwxMjMuNzIgODcuOTY3OEMxMjMuNTA1IDg4LjAzMjIgMTIzLjI1NCA4OC4wODk1IDEyMi45NjggODguMTM5NkMxMjIuNjg4IDg4LjE4OTggMTIyLjM2NiA4OC4yMTQ4IDEyMi4wMDEgODguMjE0OEMxMjEuNDA3IDg4LjIxNDggMTIwLjg4IDg4LjExMSAxMjAuNDIyIDg3LjkwMzNDMTE5Ljk2NCA4Ny42ODg1IDExOS42MDUgODcuMzQxMSAxMTkuMzQ4IDg2Ljg2MTNDMTE5LjA5IDg2LjM4MTUgMTE4Ljk2MSA4NS43NDQxIDExOC45NjEgODQuOTQ5MlY3My41MzAzWk0xMjUuMTE5IDgyLjMxNzRWODIuMDcwM0MxMjUuMTE5IDgxLjIzMjQgMTI1LjI0MSA4MC40NTU0IDEyNS40ODQgNzkuNzM5M0MxMjUuNzI4IDc5LjAxNiAxMjYuMDc5IDc4LjM4OTMgMTI2LjUzNyA3Ny44NTk0QzEyNy4wMDMgNzcuMzIyMyAxMjcuNTY4IDc2LjkwNjkgMTI4LjIzNCA3Ni42MTMzQzEyOC45MDggNzYuMzEyNSAxMjkuNjY3IDc2LjE2MjEgMTMwLjUxMiA3Ni4xNjIxQzEzMS4zNjQgNzYuMTYyMSAxMzIuMTIzIDc2LjMxMjUgMTMyLjc4OSA3Ni42MTMzQzEzMy40NjIgNzYuOTA2OSAxMzQuMDMyIDc3LjMyMjMgMTM0LjQ5NyA3Ny44NTk0QzEzNC45NjMgNzguMzg5MyAxMzUuMzE3IDc5LjAxNiAxMzUuNTYxIDc5LjczOTNDMTM1LjgwNCA4MC40NTU0IDEzNS45MjYgODEuMjMyNCAxMzUuOTI2IDgyLjA3MDNWODIuMzE3NEMxMzUuOTI2IDgzLjE1NTMgMTM1LjgwNCA4My45MzIzIDEzNS41NjEgODQuNjQ4NEMxMzUuMzE3IDg1LjM2NDYgMTM0Ljk2MyA4NS45OTEyIDEzNC40OTcgODYuNTI4M0MxMzQuMDMyIDg3LjA1ODMgMTMzLjQ2NiA4Ny40NzM2IDEzMi44IDg3Ljc3NDRDMTMyLjEzNCA4OC4wNjggMTMxLjM3OCA4OC4yMTQ4IDEzMC41MzMgODguMjE0OEMxMjkuNjgxIDg4LjIxNDggMTI4LjkxOCA4OC4wNjggMTI4LjI0NSA4Ny43NzQ0QzEyNy41NzkgODcuNDczNiAxMjcuMDEzIDg3LjA1ODMgMTI2LjU0OCA4Ni41MjgzQzEyNi4wODIgODUuOTkxMiAxMjUuNzI4IDg1LjM2NDYgMTI1LjQ4NCA4NC42NDg0QzEyNS4yNDEgODMuOTMyMyAxMjUuMTE5IDgzLjE1NTMgMTI1LjExOSA4Mi4zMTc0Wk0xMjcuNzA4IDgyLjA3MDNWODIuMzE3NEMxMjcuNzA4IDgyLjg0MDIgMTI3Ljc2MiA4My4zMzQzIDEyNy44NjkgODMuNzk5OEMxMjcuOTc3IDg0LjI2NTMgMTI4LjE0NSA4NC42NzM1IDEyOC4zNzQgODUuMDI0NEMxMjguNjAzIDg1LjM3NTMgMTI4Ljg5NyA4NS42NTEgMTI5LjI1NSA4NS44NTE2QzEyOS42MTMgODYuMDUyMSAxMzAuMDM5IDg2LjE1MjMgMTMwLjUzMyA4Ni4xNTIzQzEzMS4wMTMgODYuMTUyMyAxMzEuNDI4IDg2LjA1MjEgMTMxLjc3OSA4NS44NTE2QzEzMi4xMzcgODUuNjUxIDEzMi40MzEgODUuMzc1MyAxMzIuNjYgODUuMDI0NEMxMzIuODg5IDg0LjY3MzUgMTMzLjA1OCA4NC4yNjUzIDEzMy4xNjUgODMuNzk5OEMxMzMuMjggODMuMzM0MyAxMzMuMzM3IDgyLjg0MDIgMTMzLjMzNyA4Mi4zMTc0VjgyLjA3MDNDMTMzLjMzNyA4MS41NTQ3IDEzMy4yOCA4MS4wNjc3IDEzMy4xNjUgODAuNjA5NEMxMzMuMDU4IDgwLjE0MzkgMTMyLjg4NiA3OS43MzIxIDEzMi42NDkgNzkuMzc0QzEzMi40MiA3OS4wMTYgMTMyLjEyNyA3OC43MzY3IDEzMS43NjkgNzguNTM2MUMxMzEuNDE4IDc4LjMyODUgMTMwLjk5OSA3OC4yMjQ2IDEzMC41MTIgNzguMjI0NkMxMzAuMDI1IDc4LjIyNDYgMTI5LjYwMiA3OC4zMjg1IDEyOS4yNDQgNzguNTM2MUMxMjguODkzIDc4LjczNjcgMTI4LjYwMyA3OS4wMTYgMTI4LjM3NCA3OS4zNzRDMTI4LjE0NSA3OS43MzIxIDEyNy45NzcgODAuMTQzOSAxMjcuODY5IDgwLjYwOTRDMTI3Ljc2MiA4MS4wNjc3IDEyNy43MDggODEuNTU0NyAxMjcuNzA4IDgyLjA3MDNaTTE0MC45MTMgNzguODU4NFY4OEgxMzguMzI0Vjc2LjM3N0gxNDAuNzYzTDE0MC45MTMgNzguODU4NFpNMTQwLjQ1MSA4MS43NTg4TDEzOS42MTMgODEuNzQ4QzEzOS42MiA4MC45MjQ1IDEzOS43MzUgODAuMTY4OSAxMzkuOTU3IDc5LjQ4MTRDMTQwLjE4NiA3OC43OTM5IDE0MC41MDEgNzguMjAzMSAxNDAuOTAyIDc3LjcwOUMxNDEuMzExIDc3LjIxNDggMTQxLjc5OCA3Ni44MzUzIDE0Mi4zNjMgNzYuNTcwM0MxNDIuOTI5IDc2LjI5ODIgMTQzLjU1OSA3Ni4xNjIxIDE0NC4yNTQgNzYuMTYyMUMxNDQuODEyIDc2LjE2MjEgMTQ1LjMxNyA3Ni4yNDA5IDE0NS43NjkgNzYuMzk4NEMxNDYuMjI3IDc2LjU0ODggMTQ2LjYxNyA3Ni43OTU5IDE0Ni45MzkgNzcuMTM5NkMxNDcuMjY5IDc3LjQ4MzQgMTQ3LjUyIDc3LjkzMSAxNDcuNjkxIDc4LjQ4MjRDMTQ3Ljg2MyA3OS4wMjY3IDE0Ny45NDkgNzkuNjk2MyAxNDcuOTQ5IDgwLjQ5MTJWODhIMTQ1LjM1VjgwLjQ4MDVDMTQ1LjM1IDc5LjkyMTkgMTQ1LjI2NyA3OS40ODE0IDE0NS4xMDMgNzkuMTU5MkMxNDQuOTQ1IDc4LjgyOTggMTQ0LjcxMiA3OC41OTcgMTQ0LjQwNCA3OC40NjA5QzE0NC4xMDQgNzguMzE3NyAxNDMuNzI4IDc4LjI0NjEgMTQzLjI3NiA3OC4yNDYxQzE0Mi44MzIgNzguMjQ2MSAxNDIuNDM1IDc4LjMzOTIgMTQyLjA4NCA3OC41MjU0QzE0MS43MzMgNzguNzExNiAxNDEuNDM2IDc4Ljk2NTggMTQxLjE5MiA3OS4yODgxQzE0MC45NTYgNzkuNjEwNCAxNDAuNzczIDc5Ljk4MjcgMTQwLjY0NSA4MC40MDUzQzE0MC41MTYgODAuODI3OCAxNDAuNDUxIDgxLjI3OSAxNDAuNDUxIDgxLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", + "description": "Action button.", + "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 };\n}\n\nself.onDestroy = function() {\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "", + "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" + ] +} \ No newline at end of file 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..90d74a4c16 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -91,6 +91,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/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 f8dab27af9..a12829f7c9 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'; @@ -226,9 +228,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/config/action/custom-action-pretty-resources-tabs.component'; -import * as CustomActionPrettyEditorComponent from '@home/components/widget/config/action/custom-action-pretty-editor.component'; -import * as MobileActionEditorComponent from '@home/components/widget/config/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'; @@ -261,7 +263,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 +287,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'; @@ -541,9 +541,9 @@ 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/config/action/custom-action-pretty-resources-tabs.component': CustomActionPrettyResourcesTabsComponent, - '@home/components/widget/config/action/custom-action-pretty-editor.component': CustomActionPrettyEditorComponent, - '@home/components/widget/config/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, 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..2d143f4e16 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; 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 1eec44b714..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 @@ -38,13 +38,12 @@ import { } from '@home/components/widget/action/manage-widget-actions.component.models'; import { UtilsService } from '@core/services/utils.service'; import { - actionDescriptorToAction, + actionDescriptorToAction, defaultWidgetAction, WidgetActionSource, - WidgetActionType, widgetType } from '@shared/models/widget.models'; import { takeUntil } from 'rxjs/operators'; -import { CustomActionEditorCompleter } from '@home/components/widget/config/action/custom-action.models'; +import { CustomActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { WidgetService } from '@core/http/widget.service'; export interface WidgetActionDialogData { @@ -92,11 +91,7 @@ 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/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 4f030986f1..4ac5934e93 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 @@ -20,30 +20,36 @@
widgets.single-switch.behavior
-
widgets.value-action.initial-state
+
widgets.rpc-state.initial-state
-
widgets.value-action.turn-on
+
widgets.rpc-state.turn-on
-
widgets.value-action.turn-off
+
widgets.rpc-state.turn-off
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-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts index 7b80b4b088..4470fc1327 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts @@ -33,11 +33,6 @@ import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component'; import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; import { TargetDeviceComponent } from '@home/components/widget/config/target-device.component'; -import { WidgetActionComponent } from '@home/components/widget/config/action/widget-action.component'; -import { CustomActionPrettyResourcesTabsComponent } - from '@home/components/widget/config/action/custom-action-pretty-resources-tabs.component'; -import { CustomActionPrettyEditorComponent } from '@home/components/widget/config/action/custom-action-pretty-editor.component'; -import { MobileActionEditorComponent } from '@home/components/widget/config/action/mobile-action-editor.component'; @NgModule({ declarations: @@ -55,11 +50,7 @@ import { MobileActionEditorComponent } from '@home/components/widget/config/acti TimewindowStyleComponent, TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - WidgetSettingsComponent, - WidgetActionComponent, - CustomActionPrettyResourcesTabsComponent, - CustomActionPrettyEditorComponent, - MobileActionEditorComponent + WidgetSettingsComponent ], imports: [ CommonModule, @@ -82,11 +73,7 @@ import { MobileActionEditorComponent } from '@home/components/widget/config/acti TimewindowStylePanelComponent, TimewindowConfigPanelComponent, WidgetSettingsComponent, - WidgetSettingsCommonModule, - WidgetActionComponent, - CustomActionPrettyResourcesTabsComponent, - CustomActionPrettyEditorComponent, - MobileActionEditorComponent + WidgetSettingsCommonModule ] }) export class WidgetConfigComponentsModule { } 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.scss b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss new file mode 100644 index 0000000000..276fa725b8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.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. + */ +.tb-action-widget-error-container { + position: absolute; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 1; + .tb-action-widget-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-action-widget-error-text { + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + color: rgba(209, 39, 48, 1); + } + .tb-action-widget-error-clear { + color: rgba(209, 39, 48, 1); + } + } +} 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..f667e63356 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.html @@ -0,0 +1,35 @@ + +
+
+ +
+ + +
+
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.component.scss new file mode 100644 index 0000000000..fcdc355ef9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-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-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..f86ef73439 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/action-button-widget.models.ts @@ -0,0 +1,67 @@ +/// +/// 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, + subscribeForUpdates: false + }, + getTimeSeries: { + key: 'state', + subscribeForUpdates: false + }, + 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, + subscribeForUpdates: false + }, + getTimeSeries: { + key: 'state', + subscribeForUpdates: false + }, + 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/rpc/single-switch-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html index 04ec9f22ab..5f085ff7b5 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 @@ -33,10 +33,10 @@
-
-
-
- +
+
+
+
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..92c8f87f09 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 @@ -45,36 +45,6 @@ $switchColorDisabled: var(--tb-single-switch-color-disabled, #D5D7E5); 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); - } - } - } > div.tb-single-switch-title-panel { position: absolute; top: 12px; 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 61d4fb9322..1a7274c5b6 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 @@ -42,9 +42,8 @@ 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 { ValueType } from '@shared/models/constants'; +import { UtilsService } from '@core/services/utils.service'; const horizontalLayoutPadding = 48; const verticalLayoutPadding = 36; @@ -52,7 +51,7 @@ 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 @@ -102,9 +101,12 @@ export class SingleSwitchWidgetComponent extends 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(cd); @@ -148,25 +150,21 @@ 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.value-action.initial-state')}; + {...this.settings.initialState, actionLabel: this.ctx.translate.instant('widgets.rpc-state.initial-state')}; this.createValueGetter(getInitialStateSettings, ValueType.BOOLEAN, { next: (value) => this.onValue(value) }); const onUpdateStateSettings = {...this.settings.onUpdateState, - actionLabel: this.ctx.translate.instant('widgets.value-action.turn-on')}; + 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.value-action.turn-off')}; + actionLabel: this.ctx.translate.instant('widgets.rpc-state.turn-off')}; this.offValueSetter = this.createValueSetter(offUpdateStateSettings); } @@ -190,6 +188,9 @@ export class SingleSwitchWidgetComponent extends if (this.panelResize$) { this.panelResize$.disconnect(); } + if (this.singleSwitchCssClass) { + this.utils.clearCssElement(this.renderer, this.singleSwitchCssClass); + } super.ngOnDestroy(); } @@ -211,7 +212,6 @@ export class SingleSwitchWidgetComponent extends } private onValue(value: boolean): void { - console.log(`onValue: ${value}`); this.value = !!value; this.cd.markForCheck(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/value-action-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/action/value-action-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/action-settings-button.component.html index 1e54dc3270..3290099ff9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/value-action-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/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/config/action/widget-action.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts similarity index 99% rename from ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts index ed7690fe28..c93a05fafd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/action/widget-action.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts @@ -44,7 +44,7 @@ import { PopoverPlacement, PopoverPlacements } from '@shared/components/popover. import { CustomActionEditorCompleter, toCustomAction -} from '@home/components/widget/config/action/custom-action.models'; +} from '@home/components/widget/lib/settings/common/action/custom-action.models'; const stateDisplayTypes = ['normal', 'separateDialog', 'popover'] as const; type stateDisplayTypeTuple = typeof stateDisplayTypes; 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..b4ce9c8ce2 --- /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..7e4a316eef --- /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,93 @@ + +
+
{{ 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..419ce7e4d4 --- /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,171 @@ +/// +/// 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, 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'; + +@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 { + + @Input() + appearance: WidgetButtonAppearance; + + @Input() + borderRadius: string; + + @Input() + state: WidgetButtonState; + + @Input() + customStyle: WidgetButtonCustomStyle; + + @Input() + popover: TbPopoverComponent; + + @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 ? false : true; + 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 = {...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..b9a1605b19 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts @@ -0,0 +1,157 @@ +/// +/// 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'; + +@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 = {...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/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index a0405d3254..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 @@ -67,6 +67,31 @@ import { 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: [ @@ -93,7 +118,16 @@ import { CssSizeInputComponent } from '@home/components/widget/lib/settings/comm GetValueActionSettingsPanelComponent, DeviceKeyAutocompleteComponent, SetValueActionSettingsComponent, - SetValueActionSettingsPanelComponent + SetValueActionSettingsPanelComponent, + WidgetActionComponent, + CustomActionPrettyResourcesTabsComponent, + CustomActionPrettyEditorComponent, + MobileActionEditorComponent, + WidgetActionSettingsComponent, + WidgetActionSettingsPanelComponent, + WidgetButtonAppearanceComponent, + WidgetButtonCustomStyleComponent, + WidgetButtonCustomStylePanelComponent ], imports: [ CommonModule, @@ -124,7 +158,16 @@ import { CssSizeInputComponent } from '@home/components/widget/lib/settings/comm GetValueActionSettingsPanelComponent, DeviceKeyAutocompleteComponent, SetValueActionSettingsComponent, - SetValueActionSettingsPanelComponent + 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/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 4cdb0be38e..9bdca64bbd 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 @@ -19,30 +19,36 @@
widgets.single-switch.behavior
-
widgets.value-action.initial-state
+
widgets.rpc-state.initial-state
-
widgets.value-action.turn-on
+
widgets.rpc-state.turn-on
-
widgets.value-action.turn-off
+
widgets.rpc-state.turn-off
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..f4bd08a20c 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,7 +15,7 @@ /// 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'; @@ -37,6 +37,10 @@ export class SingleSwitchWidgetSettingsComponent extends WidgetSettingsComponent return this.widget?.config?.targetDevice; } + get widgetType(): widgetType { + return this.widget?.type; + } + singleSwitchLayouts = singleSwitchLayouts; singleSwitchLayoutTranslationMap = singleSwitchLayoutTranslations; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index 377ab8b6ea..6037a4ea43 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -565,6 +565,9 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.embedTitlePanel)) { result.typeParameters.embedTitlePanel = false; } + if (isUndefined(result.typeParameters.overflowVisible)) { + result.typeParameters.overflowVisible = false; + } if (isUndefined(result.typeParameters.hideDataSettings)) { result.typeParameters.hideDataSettings = false; } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 82a4856f4a..11bf6871f8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -71,6 +71,7 @@ import { BarChartWithLabelsWidgetComponent } from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.component'; import { SingleSwitchWidgetComponent } from '@home/components/widget/lib/rpc/single-switch-widget.component'; +import { ActionButtonWidgetComponent } from '@home/components/widget/lib/button/action-button-widget.component'; @NgModule({ declarations: @@ -114,7 +115,8 @@ import { SingleSwitchWidgetComponent } from '@home/components/widget/lib/rpc/sin DoughnutWidgetComponent, RangeChartWidgetComponent, BarChartWithLabelsWidgetComponent, - SingleSwitchWidgetComponent + SingleSwitchWidgetComponent, + ActionButtonWidgetComponent ], imports: [ CommonModule, @@ -162,7 +164,8 @@ import { SingleSwitchWidgetComponent } from '@home/components/widget/lib/rpc/sin DoughnutWidgetComponent, RangeChartWidgetComponent, BarChartWithLabelsWidgetComponent, - SingleSwitchWidgetComponent + SingleSwitchWidgetComponent, + ActionButtonWidgetComponent ], providers: [ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html index 4f428dd983..7235b300bc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html @@ -24,6 +24,7 @@ 'tb-highlighted': isHighlighted(widget), 'tb-not-highlighted': isNotHighlighted(widget), 'mat-elevation-z4': widget.dropShadow, + 'tb-overflow-visible': widgetComponent.widgetContext?.overflowVisible, 'tb-has-timewindow': widget.hasTimewindow, 'tb-edit': isEdit }" @@ -88,6 +89,7 @@ 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 1c029b6524..15e982c060 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 = {}; @@ -1423,7 +1429,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/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 4a7fc32502..a834120dc6 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 @@ -263,6 +263,7 @@ export class WidgetContext { height: number; $scope: IDynamicWidgetComponent; isEdit: boolean; + isPreview: boolean; isMobile: boolean; toastTargetId: string; @@ -279,6 +280,7 @@ export class WidgetContext { timeWindow?: WidgetTimewindow; embedTitlePanel?: boolean; + overflowVisible?: boolean; hideTitlePanel = false; 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..9b239db068 --- /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..06c875de7f --- /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, #263BD7); // 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, #2234BD); // 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, #1D2DA3); // 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, #2234BD); // 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-activated, $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..d8fe88b48e --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.ts @@ -0,0 +1,178 @@ +/// +/// 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'; + +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; + + @Output() + clicked = new EventEmitter(); + + 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(); + } + + private updateAppearance(): void { + this.clearAppearanceCss(); + if (this.appearance.showIcon) { + this.iconStyle = iconStyle(this.appearance.iconSize, this.appearance.iconSizeUnit); + } + 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..616af28588 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button.models.ts @@ -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. +/// + +import { cssUnit } from '@shared/models/widget-settings.models'; +import tinycolor from 'tinycolor2'; + +const defaultMainColor = '#3F52DD'; +const defaultBackgroundColor = '#FFFFFF'; + +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, 6); + 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, 12); + const mainColorInstance = tinycolor(mainColor); + const mainColorPressedRipple = mainColorInstance.setAlpha(mainColorInstance.getAlpha() * 0.1).toRgbString(); + const mainColorPressedRippleFilled = darkenColor(mainColor, 18); + 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, 12); + 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); + return input.darken(amount).toRgbString(); +}; 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 index d1442c09c5..c9a61c5ad0 100644 --- a/ui-ngx/src/app/shared/models/action-widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/action-widget-settings.models.ts @@ -15,6 +15,7 @@ /// import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { widgetType } from '@shared/models/widget.models'; export enum GetValueAction { DO_NOTHING = 'DO_NOTHING', @@ -25,6 +26,14 @@ export enum GetValueAction { 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'], @@ -75,7 +84,7 @@ export interface ValueActionSettings { export interface GetValueSettings extends ValueActionSettings { action: GetValueAction; defaultValue: V; - executeRpc: RpcSettings; + executeRpc?: RpcSettings; getAttribute: GetAttributeValueSettings; getTimeSeries: GetTelemetryValueSettings; dataToValue: DataToValueSettings; @@ -89,6 +98,14 @@ export enum SetValueAction { 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'], 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 cf72e20025..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'; @@ -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 3a13f51b24..625d957cff 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' @@ -675,6 +693,14 @@ export const actionDescriptorToAction = (descriptor: WidgetActionDescriptor): Wi 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/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e18d21f609..fa9945dd50 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5182,6 +5182,42 @@ "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 performed when the button is clicked." + }, + "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": "Condition under which the button is active.", + "disabled-state": "Disabled state", + "disabled-state-hint": "Condition under which the button is disabled.", + "enabled": "Enabled", + "hovered": "Hovered", + "pressed": "Pressed", + "activated": "Activated", + "disabled": "Disabled" + }, "background": { "background": "Background", "background-settings": "Background settings", @@ -6483,7 +6519,7 @@ "source-entity-alias": "Source entity alias", "source-entity-attribute": "Source entity attribute" }, - "value-action": { + "rpc-state": { "initial-state": "Initial state", "initial-state-hint": "Action to get the initial value of the component.", "turn-on": "Turn 'On'", @@ -6491,7 +6527,9 @@ "turn-off": "Turn 'Off'", "turn-off-hint": "Action performed to turn OFF the component.", "on": "On", - "off": "Off", + "off": "Off" + }, + "value-action": { "do-nothing": "Do nothing", "execute-rpc": "Execute RPC", "get-attribute": "Get attribute", @@ -6530,7 +6568,7 @@ "converter-function": "Function", "converter-constant": "Constant", "parse-value-function": "Parse value function", - "on-when-result-is": "'On' when result is", + "state-when-result-is": "'{{state}}' when result is", "parameters": "Parameters", "convert-value-function": "Convert value function", "error": { @@ -6785,7 +6823,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" : { 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/form.scss b/ui-ngx/src/form.scss index d6fceb20f7..635d330572 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -607,6 +607,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; From bf9c4af2aa738655dcbfc5dabde934184b38cbc2 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 1 Feb 2024 20:23:12 +0100 Subject: [PATCH 075/128] fixed infinit tell failure --- .../AbstractTbRuleEngineSubmitStrategy.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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 3801e43b7f..6f9a4c49c6 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,18 @@ public abstract class AbstractTbRuleEngineSubmitStrategy implements TbRuleEngine List> newOrderedMsgList = new ArrayList<>(reprocessMap.size()); for (IdMsgPair pair : orderedMsgList) { if (reprocessMap.containsKey(pair.uuid)) { - newOrderedMsgList.add(pair); + var oldValue = pair.getMsg().getValue(); + if (StringUtils.isNotEmpty(oldValue.getFailureMessage())) { + var newValue = TransportProtos.ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(oldValue.getTenantIdMSB()) + .setTenantIdLSB(oldValue.getTenantIdLSB()) + .setTbMsg(oldValue.getTbMsg()) + .build(); + var newMsg = new TbProtoQueueMsg<>(pair.getMsg().getKey(), newValue, pair.getMsg().getHeaders()); + newOrderedMsgList.add(new IdMsgPair<>(pair.getUuid(), newMsg)); + } else { + newOrderedMsgList.add(pair); + } } } orderedMsgList = newOrderedMsgList; From d287d410abf74f13b866885cf774d6402f23dbf5 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 31 Jan 2024 15:47:07 +0100 Subject: [PATCH 076/128] GitHub workflow license-header-format --- .github/workflows/license-header-format.yml | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/license-header-format.yml 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 From 42edd481e7795b8f5401db70b09697472b83b020 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 2 Feb 2024 13:53:41 +0200 Subject: [PATCH 077/128] UI: Add advanced settings for action button. Update Get attribute/time-series value action to always subscribe for updates. --- .../system/widget_types/action_button.json | 4 +- .../widget/lib/action/action-widget.models.ts | 42 ++--------- .../lib/button/action-button-widget.models.ts | 12 ++-- .../lib/rpc/single-switch-widget.models.ts | 6 +- ...tion-button-widget-settings.component.html | 51 ++++++++++++++ ...action-button-widget-settings.component.ts | 70 +++++++++++++++++++ ...value-action-settings-panel.component.html | 10 --- ...t-value-action-settings-panel.component.ts | 8 +-- ...led-indicator-widget-settings.component.ts | 2 +- .../round-switch-widget-settings.component.ts | 2 +- ...single-switch-widget-settings.component.ts | 9 ++- .../slide-toggle-widget-settings.component.ts | 2 +- ...witch-control-widget-settings.component.ts | 2 +- .../lib/settings/widget-settings.module.ts | 20 ++++-- .../models/action-widget-settings.models.ts | 18 +---- .../assets/locale/locale.constant-en_US.json | 2 - 16 files changed, 161 insertions(+), 99 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/action-button-widget-settings.component.ts 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 index 3ad518ce23..e6ffa8084e 100644 --- a/application/src/main/data/json/system/widget_types/action_button.json +++ b/application/src/main/data/json/system/widget_types/action_button.json @@ -11,10 +11,10 @@ "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 };\n}\n\nself.onDestroy = function() {\n}\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": "", + "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\"}" 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 index 8557c9cbd3..26dc76866a 100644 --- 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 @@ -31,7 +31,6 @@ import { DataToValueSettings, DataToValueType, GetAttributeValueSettings, - GetTelemetryValueSettings, GetValueAction, GetValueSettings, RpcSettings, @@ -405,7 +404,7 @@ export class ExecuteRpcValueGetter extends ValueGetter { } } -export abstract class TelemetryValueGetter extends ValueGetter { +export abstract class TelemetryValueGetter extends ValueGetter { protected targetEntityId: EntityId; private telemetrySubscriber: TelemetrySubscriber; @@ -428,11 +427,7 @@ export abstract class TelemetryValueGetter err); } - if (this.getTelemetryValueSettings().subscribeForUpdates) { - return this.subscribeForTelemetryValue(); - } else { - return this.doGetTelemetryValue(); - } + return this.subscribeForTelemetryValue(); } else { return of(null); } @@ -464,8 +459,6 @@ export abstract class TelemetryValueGetter; - destroy() { if (this.telemetrySubscriber) { this.telemetrySubscriber.unsubscribe(); @@ -492,17 +485,9 @@ export class AttributeValueGetter extends TelemetryValueGetter { - const getAttributeValueSettings = this.getTelemetryValueSettings(); - return this.ctx.attributeService.getEntityAttributes(this.targetEntityId, - getAttributeValueSettings.scope, [getAttributeValueSettings.key], {ignoreLoading: true, ignoreErrors: true}).pipe( - map((data) => data.find(attr => attr.key === getAttributeValueSettings.key)?.value) - ); - } - } -export class TimeSeriesValueGetter extends TelemetryValueGetter { +export class TimeSeriesValueGetter extends TelemetryValueGetter { constructor(protected ctx: WidgetContext, protected settings: GetValueSettings, @@ -511,28 +496,9 @@ export class TimeSeriesValueGetter extends TelemetryValueGetter { - const getTelemetryValueSettings = this.getTelemetryValueSettings(); - return this.ctx.attributeService.getEntityTimeseriesLatest(this.targetEntityId, - [getTelemetryValueSettings.key], true, {ignoreLoading: true, ignoreErrors: true}) - .pipe( - map((data) => { - let value: any = null; - if (data[getTelemetryValueSettings.key]) { - const dataSet = data[getTelemetryValueSettings.key]; - if (dataSet.length) { - value = dataSet[0].value; - } - } - return value; - }) - ); - } - } export class ExecuteRpcValueSetter extends ValueSetter { 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 index f86ef73439..4b3fafa55c 100644 --- 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 @@ -33,12 +33,10 @@ export const actionButtonDefaultSettings: ActionButtonWidgetSettings = { defaultValue: false, getAttribute: { key: 'state', - scope: null, - subscribeForUpdates: false + scope: null }, getTimeSeries: { - key: 'state', - subscribeForUpdates: false + key: 'state' }, dataToValue: { type: DataToValueType.NONE, @@ -51,12 +49,10 @@ export const actionButtonDefaultSettings: ActionButtonWidgetSettings = { defaultValue: false, getAttribute: { key: 'state', - scope: null, - subscribeForUpdates: false + scope: null }, getTimeSeries: { - key: 'state', - subscribeForUpdates: false + key: 'state' }, dataToValue: { type: DataToValueType.NONE, 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 23510e174a..4f0401b3bd 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 @@ -93,12 +93,10 @@ export const singleSwitchDefaultSettings: SingleSwitchWidgetSettings = { }, getAttribute: { key: 'state', - scope: null, - subscribeForUpdates: false + scope: null }, getTimeSeries: { - key: 'state', - subscribeForUpdates: false + key: 'state' }, dataToValue: { type: DataToValueType.NONE, 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/common/action/get-value-action-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html index 9684be6fd3..faad403e9b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/get-value-action-settings-panel.component.html @@ -84,11 +84,6 @@ [attributeScope]="getValueSettingsFormGroup.get('getAttribute').get('scope').value">
-
- - {{ 'widgets.value-action.subscribe-for-updates' | translate }} - -
@@ -106,11 +101,6 @@ [keyType]="dataKeyType.timeseries">
-
- - {{ 'widgets.value-action.subscribe-for-updates' | translate }} - -
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 index 4b9eb98ae2..f9202b982f 100644 --- 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 @@ -24,7 +24,7 @@ import { merge } from 'rxjs'; import { DataToValueType, GetValueAction, - getValueActions, getValueActionsByWidgetType, + getValueActionsByWidgetType, getValueActionTranslations, GetValueSettings } from '@shared/models/action-widget-settings.models'; @@ -117,12 +117,10 @@ export class GetValueActionSettingsPanelComponent extends PageComponent implemen }), getAttribute: this.fb.group({ scope: [this.getValueSettings?.getAttribute?.scope, []], - key: [this.getValueSettings?.getAttribute?.key, [Validators.required]], - subscribeForUpdates: [this.getValueSettings?.getAttribute?.subscribeForUpdates, []] + key: [this.getValueSettings?.getAttribute?.key, [Validators.required]] }), getTimeSeries: this.fb.group({ - key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]], - subscribeForUpdates: [this.getValueSettings?.getTimeSeries?.subscribeForUpdates, []] + key: [this.getValueSettings?.getTimeSeries?.key, [Validators.required]] }), dataToValue: this.fb.group({ type: [this.getValueSettings?.dataToValue?.type, [Validators.required]], 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.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/single-switch-widget-settings.component.ts index f4bd08a20c..8c7282e32b 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 @@ -20,7 +20,8 @@ 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,11 +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.widget?.type; + return this.widgetConfig?.widgetType; } singleSwitchLayouts = singleSwitchLayouts; @@ -156,6 +157,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/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/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 30c1b748de..607cb91318 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,9 @@ 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'; @NgModule({ declarations: [ @@ -424,7 +431,8 @@ import { DoughnutWidgetSettingsComponent, RangeChartWidgetSettingsComponent, BarChartWithLabelsWidgetSettingsComponent, - SingleSwitchWidgetSettingsComponent + SingleSwitchWidgetSettingsComponent, + ActionButtonWidgetSettingsComponent ], imports: [ CommonModule, @@ -543,7 +551,8 @@ import { DoughnutWidgetSettingsComponent, RangeChartWidgetSettingsComponent, BarChartWithLabelsWidgetSettingsComponent, - SingleSwitchWidgetSettingsComponent + SingleSwitchWidgetSettingsComponent, + ActionButtonWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -629,5 +638,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type extends ValueActionSettings { defaultValue: V; executeRpc?: RpcSettings; getAttribute: GetAttributeValueSettings; - getTimeSeries: GetTelemetryValueSettings; + getTimeSeries: TelemetryValueSettings; dataToValue: DataToValueSettings; } @@ -133,13 +129,3 @@ export interface SetValueSettings extends ValueActionSettings { putTimeSeries: TelemetryValueSettings; valueToData: ValueToDataSettings; } - -/*export interface RpcStateBehaviourSettings { - initialState: RpcInitialStateSettings; - updateStateByValue: (value: V) => RpcUpdateStateSettings; -} - -export interface RpcStateWidgetSettings { - initialState: RpcInitialStateSettings; - background: BackgroundSettings; -}*/ 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 fa9945dd50..c9108e9bac 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6561,8 +6561,6 @@ "attribute-key-required": "Attribute key is required.", "time-series-key": "Time-series key", "time-series-key-required": "Time-series key is required.", - "subscribe-for-updates": "Subscribe for updates", - "subscribe-for-updates-hint": "Subscribe for updates", "action-result-converter": "Action result converter", "converter-none": "None", "converter-function": "Function", From 53b57b1351143ed6f23422e7f983e563b3860fb1 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 2 Feb 2024 14:45:28 +0200 Subject: [PATCH 078/128] updated CassandraBaseTimeseriesDao to do one db insert instead of 5 when setNullValuesEnabled=true --- .../CassandraBaseTimeseriesDao.java | 142 ++++++++++-------- .../timeseries/BaseTimeseriesServiceTest.java | 35 ++++- ...eseriesServiceNoSqlSetNullEnabledTest.java | 72 +++++++++ .../nosql/TimeseriesServiceNoSqlTest.java | 96 ++++++++++++ 4 files changed, 281 insertions(+), 64 deletions(-) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/timeseries/nosql/TimeseriesServiceNoSqlSetNullEnabledTest.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 31e29434e8..8d96dd293b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -65,6 +65,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -117,6 +118,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 +162,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 +469,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 +561,56 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD return deleteStmt; } + private PreparedStatement getSaveWithNullStmt() { + if (saveWithNullStmt == null) { + stmtCreationLock.lock(); + try { + if (saveWithNullStmt == null) { + saveWithNullStmt = prepare(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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + } + } finally { + stmtCreationLock.unlock(); + } + } + return saveWithNullStmt; + } + + private PreparedStatement getSaveWithNullWithTtlStmt() { + if (saveWithNullWithTtlStmt == null) { + stmtCreationLock.lock(); + try { + if (saveWithNullWithTtlStmt == null) { + saveWithNullWithTtlStmt = prepare(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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) USING TTL ?"); + } + } finally { + stmtCreationLock.unlock(); + } + } + return saveWithNullWithTtlStmt; + } + private PreparedStatement getSaveStmt(DataType dataType) { if (saveStmts == null) { stmtCreationLock.lock(); 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..b06394e8d2 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,29 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { assertEquals(3, list.size()); } + @Test + public void shouldSaveEntryOfEachType() throws Exception { + 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()); + 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()); + + 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()); + } + 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 +717,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..82916aad1c 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,105 @@ */ 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)); + 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 shouldDeleteValueAfterTtl() throws ExecutionException, InterruptedException, TimeoutException { + long longEntryTs = TimeUnit.MINUTES.toMillis(2); + long doubleEntryTs = TimeUnit.MINUTES.toMillis(3); + long ttlInSec = TimeUnit.SECONDS.toSeconds(3); + long longValue = 0L; + TsKvEntry longEntry = new BasicTsKvEntry(longEntryTs, new LongDataEntry("temp", longValue)); + double doubleValue = 20.6; + TsKvEntry doubleEntry = new BasicTsKvEntry(doubleEntryTs, new DoubleDataEntry("temp", doubleValue)); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + tsService.save(tenantId, deviceId, longEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); + tsService.save(tenantId, deviceId, Collections.singletonList(doubleEntry), ttlInSec).get(MAX_TIMEOUT, TimeUnit.SECONDS); + + Thread.sleep(TimeUnit.SECONDS.toMillis(ttlInSec)); + List listAfterTtl = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, + doubleEntryTs + 1 , 1000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + assertEquals(1, listAfterTtl.size()); + assertTrue(listAfterTtl.get(0).getLongValue().isPresent()); + assertThat(listAfterTtl.get(0).getLongValue().get()).isEqualTo(longValue); + assertFalse(listAfterTtl.get(0).getDoubleValue().isPresent()); + } + + @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); + } } From 100cdd5cf18f12859940db9284c38f17829a0473 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 2 Feb 2024 14:00:30 +0100 Subject: [PATCH 079/128] refactored due to comments --- .../AbstractTbRuleEngineSubmitStrategy.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 6f9a4c49c6..bfec9f747f 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 @@ -52,14 +52,12 @@ public abstract class AbstractTbRuleEngineSubmitStrategy implements TbRuleEngine List> newOrderedMsgList = new ArrayList<>(reprocessMap.size()); for (IdMsgPair pair : orderedMsgList) { if (reprocessMap.containsKey(pair.uuid)) { - var oldValue = pair.getMsg().getValue(); - if (StringUtils.isNotEmpty(oldValue.getFailureMessage())) { - var newValue = TransportProtos.ToRuleEngineMsg.newBuilder() - .setTenantIdMSB(oldValue.getTenantIdMSB()) - .setTenantIdLSB(oldValue.getTenantIdLSB()) - .setTbMsg(oldValue.getTbMsg()) + 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(), newValue, pair.getMsg().getHeaders()); + var newMsg = new TbProtoQueueMsg<>(pair.getMsg().getKey(), toRuleEngineMsg, pair.getMsg().getHeaders()); newOrderedMsgList.add(new IdMsgPair<>(pair.getUuid(), newMsg)); } else { newOrderedMsgList.add(pair); From bb933a4d4e0c76d054792e20eae421ed8a65a1e0 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 2 Feb 2024 15:07:15 +0200 Subject: [PATCH 080/128] Changed share modules logic --- ui-ngx/src/app/modules/common/modules-map.ts | 8 ++++++++ .../home/components/home-components.module.ts | 15 ++------------- .../src/app/modules/home/components/public-api.ts | 4 ++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index b19a588d8b..84676b9661 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -248,6 +248,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'; @@ -571,6 +575,10 @@ class ModulesMap implements IModulesMap { '@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/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 5d1b2d12f0..6411485f14 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 @@ -172,10 +172,7 @@ import { ManageWidgetActionsDialogComponent } from '@home/components/widget/action/manage-widget-actions-dialog.component'; import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module'; -import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module'; import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; -import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; -import { WidgetComponentsModule } from '@home/components/widget/widget-components.module'; @NgModule({ declarations: @@ -327,16 +324,9 @@ import { WidgetComponentsModule } from '@home/components/widget/widget-component SnmpDeviceProfileTransportModule, StatesControllerModule, DeviceCredentialsModule, - DeviceProfileCommonModule, - WidgetSettingsCommonModule, - WidgetComponentsModule, - BasicWidgetConfigModule + DeviceProfileCommonModule ], exports: [ - WidgetComponentsModule, - SharedHomeComponentsModule, - WidgetSettingsCommonModule, - WidgetConfigComponentsModule, RouterTabsComponent, EntitiesTableComponent, AddEntityDialogComponent, @@ -456,8 +446,7 @@ import { WidgetComponentsModule } from '@home/components/widget/widget-component RateLimitsComponent, RateLimitsTextComponent, RateLimitsDetailsDialogComponent, - SendNotificationButtonComponent, - BasicWidgetConfigModule + SendNotificationButtonComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index 22748a828c..e5e1fd068d 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -15,6 +15,10 @@ /// export * from './home-components.module'; +export * from './widget/config/basic/basic-widget-config.module'; +export * from './widget/lib/settings/common/widget-settings-common.module'; +export * from './widget/widget-components.module'; +export * from './widget/config/widget-config-components.module'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; From 8ce6457ce525713dfe921cbc38f51c355b84fe00 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 2 Feb 2024 15:15:40 +0200 Subject: [PATCH 081/128] Refactoring --- ui-ngx/src/app/modules/common/modules-map.ts | 2 +- .../src/app/modules/home/components/home-components.module.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 84676b9661..50714ec2c3 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -251,7 +251,7 @@ import * as SharedHomeComponentsModule from '@home/components/shared-home-compon 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 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'; 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 6411485f14..e8374f6534 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 @@ -172,6 +172,7 @@ import { ManageWidgetActionsDialogComponent } from '@home/components/widget/action/manage-widget-actions-dialog.component'; import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module'; +import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module'; import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; @NgModule({ @@ -320,6 +321,7 @@ import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delet SharedModule, SharedHomeComponentsModule, WidgetConfigComponentsModule, + BasicWidgetConfigModule, Lwm2mProfileComponentsModule, SnmpDeviceProfileTransportModule, StatesControllerModule, From e3883413a1ce52ece9b89cb7b20f816b78d12a79 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Fri, 26 Jan 2024 15:48:01 +0200 Subject: [PATCH 082/128] Make saveRuleChainMetaData @Transactional --- .../org/thingsboard/server/dao/rule/BaseRuleChainService.java | 1 + 1 file changed, 1 insertion(+) 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 6257bb0420..aaa274db4a 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()); From 9fa357735a7a327082e32dd883e79f63220453c5 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 2 Feb 2024 15:55:18 +0200 Subject: [PATCH 083/128] timeseries test improvements --- .../timeseries/BaseTimeseriesServiceTest.java | 15 +++++++----- .../nosql/TimeseriesServiceNoSqlTest.java | 24 +------------------ 2 files changed, 10 insertions(+), 29 deletions(-) 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 b06394e8d2..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 @@ -678,12 +678,12 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { @Test public void shouldSaveEntryOfEachType() throws Exception { - 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\"}"))); + 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) { @@ -693,10 +693,13 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { 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 { 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 82916aad1c..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 @@ -62,34 +62,12 @@ public class TimeseriesServiceNoSqlTest extends BaseTimeseriesServiceTest { assertEquals(5, fullList.size()); // check entries after ttl - Thread.sleep(TimeUnit.SECONDS.toMillis(ttlInSec)); + 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 shouldDeleteValueAfterTtl() throws ExecutionException, InterruptedException, TimeoutException { - long longEntryTs = TimeUnit.MINUTES.toMillis(2); - long doubleEntryTs = TimeUnit.MINUTES.toMillis(3); - long ttlInSec = TimeUnit.SECONDS.toSeconds(3); - long longValue = 0L; - TsKvEntry longEntry = new BasicTsKvEntry(longEntryTs, new LongDataEntry("temp", longValue)); - double doubleValue = 20.6; - TsKvEntry doubleEntry = new BasicTsKvEntry(doubleEntryTs, new DoubleDataEntry("temp", doubleValue)); - DeviceId deviceId = new DeviceId(Uuids.timeBased()); - tsService.save(tenantId, deviceId, longEntry).get(MAX_TIMEOUT, TimeUnit.SECONDS); - tsService.save(tenantId, deviceId, Collections.singletonList(doubleEntry), ttlInSec).get(MAX_TIMEOUT, TimeUnit.SECONDS); - - Thread.sleep(TimeUnit.SECONDS.toMillis(ttlInSec)); - List listAfterTtl = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery("temp", 0L, - doubleEntryTs + 1 , 1000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS); - assertEquals(1, listAfterTtl.size()); - assertTrue(listAfterTtl.get(0).getLongValue().isPresent()); - assertThat(listAfterTtl.get(0).getLongValue().get()).isEqualTo(longValue); - assertFalse(listAfterTtl.get(0).getDoubleValue().isPresent()); - } - @Test public void testNullValuesOfNoneTargetColumn() throws ExecutionException, InterruptedException, TimeoutException { long ts = TimeUnit.MINUTES.toMillis(1); From ba600a8bd88d2ad3fdef71a0065497573909f781 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 2 Feb 2024 18:27:54 +0200 Subject: [PATCH 084/128] UI: Widget button color calculations improvements. Improve color picker panel overflow. --- .../widget/config/data-keys.component.ts | 2 +- .../button/widget-button.component.scss | 8 +++---- .../components/button/widget-button.models.ts | 22 ++++++++++++++----- .../components/color-input.component.ts | 2 +- .../color-picker/color-picker.component.scss | 2 ++ 5 files changed, 25 insertions(+), 11 deletions(-) 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/shared/components/button/widget-button.component.scss b/ui-ngx/src/app/shared/components/button/widget-button.component.scss index 06c875de7f..546a3bed19 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.component.scss +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.scss @@ -28,19 +28,19 @@ $boxShadowColorEnabled: var(--tb-widget-button-box-shadow-color-enabled, $defaul $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, #263BD7); // main.darken(6) +$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, #2234BD); // main.darken(12) +$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, #1D2DA3); // main.darken(18) +$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, #2234BD); // main.darken(12) +$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); 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 index 616af28588..34f13864a9 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.models.ts +++ b/ui-ngx/src/app/shared/components/button/widget-button.models.ts @@ -20,6 +20,11 @@ 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)'; @@ -181,7 +186,7 @@ class HoveredButtonStateCssGenerator extends ButtonStateCssGenerator { } protected generateAdditionalStateCss(mainColor: string): string { - const mainColorHoveredFilled = darkenColor(mainColor, 6); + const mainColorHoveredFilled = darkenColor(mainColor, hoveredFilledDarkenAmount); return `--tb-widget-button-main-color-hovered-filled: ${mainColorHoveredFilled};`; } } @@ -193,10 +198,10 @@ class PressedButtonStateCssGenerator extends ButtonStateCssGenerator { } protected generateAdditionalStateCss(mainColor: string): string { - const mainColorPressedFilled = darkenColor(mainColor, 12); + const mainColorPressedFilled = darkenColor(mainColor, pressedFilledDarkenAmount); const mainColorInstance = tinycolor(mainColor); const mainColorPressedRipple = mainColorInstance.setAlpha(mainColorInstance.getAlpha() * 0.1).toRgbString(); - const mainColorPressedRippleFilled = darkenColor(mainColor, 18); + 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};`; @@ -210,7 +215,7 @@ class ActivatedButtonStateCssGenerator extends ButtonStateCssGenerator { } protected generateAdditionalStateCss(mainColor: string): string { - const mainColorActivatedFilled = darkenColor(mainColor, 12); + const mainColorActivatedFilled = darkenColor(mainColor, activatedFilledDarkenAmount); return `--tb-widget-button-main-color-activated-filled: ${mainColorActivatedFilled};`; } } @@ -255,5 +260,12 @@ export const generateWidgetButtonAppearanceCss = (appearance: WidgetButtonAppear const darkenColor = (inputColor: string, amount: number): string => { const input = tinycolor(inputColor); - return input.darken(amount).toRgbString(); + 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; } From afed69e7b526198210da65ed25535d4f4a845370 Mon Sep 17 00:00:00 2001 From: rusikv Date: Fri, 2 Feb 2024 18:38:30 +0200 Subject: [PATCH 085/128] UI: fixed dashboard entity filter remebers user input on close and cancel --- .../modules/home/components/filter/filters-edit.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, From 8cdde9e67c3523b48436aba4466caae0c95f0da9 Mon Sep 17 00:00:00 2001 From: artem Date: Sat, 3 Feb 2024 21:59:33 +0200 Subject: [PATCH 086/128] TbGpsGeofencingActionNodeTest: added extends AbstractRuleNodeUpgradeTest added @ExtendWith(MockitoExtension.class) and refactored --- .../geo/TbGpsGeofencingActionNodeTest.java | 105 ++++++++---------- 1 file changed, 47 insertions(+), 58 deletions(-) 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 index d61688d5a9..fe78f57f4e 100644 --- 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 @@ -15,40 +15,37 @@ */ package org.thingsboard.rule.engine.geo; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.Futures; 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.ArgumentCaptor; +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.DataConstants; 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.data.util.TbPair; 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.Optional; import java.util.UUID; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; +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; @@ -57,16 +54,17 @@ 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; -class TbGpsGeofencingActionNodeTest { +@ExtendWith(MockitoExtension.class) +class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { + @Mock private TbContext ctx; - private TbGpsGeofencingActionNode node; + @Mock private AttributesService attributesService; + private TbGpsGeofencingActionNode node; @BeforeEach void setUp() { - ctx = mock(TbContext.class); - attributesService = mock(AttributesService.class); - node = new TbGpsGeofencingActionNode(); + node = spy(new TbGpsGeofencingActionNode()); } @AfterEach @@ -80,23 +78,37 @@ class TbGpsGeofencingActionNodeTest { 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), + 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), + 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), + 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) + 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) ); } @@ -117,10 +129,6 @@ class TbGpsGeofencingActionNodeTest { getOutsideRectangleTbMsg(gpsGeofencingActionTestCase.getEntityId()); when(ctx.getAttributesService()).thenReturn(attributesService); - when(ctx - .getAttributesService() - .find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getServiceId())) - .thenReturn(Futures.immediateFuture(Optional.empty())); ReflectionTestUtils.setField(node, "entityStates", gpsGeofencingActionTestCase.getEntityStates()); @@ -128,24 +136,16 @@ class TbGpsGeofencingActionNodeTest { 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()); - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); if (SUCCESS.equals(expectedOutput)) { - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(eq(msg)); } else { - verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(expectedOutput)); + verify(ctx).tellNext(eq(msg), eq(expectedOutput)); } - - var actualMsg = newMsgCaptor.getValue(); - assertThat(actualMsg).isNotNull(); - assertThat(actualMsg).isSameAs(msg); - assertThat(actualMsg.getType()).isSameAs(msg.getType()); - assertThat(actualMsg.getData()).isSameAs(msg.getData()); - assertThat(actualMsg.getMetaData()).isEqualTo(msg.getMetaData()); - assertThat(actualMsg.getOriginator()).isSameAs(msg.getOriginator()); } private TbMsg getOutsideRectangleTbMsg(EntityId entityId) { @@ -250,20 +250,9 @@ class TbGpsGeofencingActionNodeTest { ); } - @ParameterizedTest - @MethodSource - void givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig(int givenVersion, String givenConfigStr, boolean hasChanges, String expectedConfigStr) throws TbNodeException { - // GIVEN - JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); - JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); - - // WHEN - TbPair upgradeResult = node.upgrade(givenVersion, givenConfig); - - // THEN - assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); - ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); - assertThat(upgradedConfig).isEqualTo(expectedConfig); + @Override + protected TbNode getTestNode() { + return node; } } From b0b8e56c97d63f2dcd6fc8b00706ef3ba0d3f312 Mon Sep 17 00:00:00 2001 From: artem Date: Sat, 3 Feb 2024 22:04:20 +0200 Subject: [PATCH 087/128] small changes --- .../thingsboard/rule/engine/geo/GpsGeofencingActionTestCase.java | 1 + .../rule/engine/geo/TbGpsGeofencingActionNodeTest.java | 1 + 2 files changed, 2 insertions(+) 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 index 8444c9cf0d..804d30d28f 100644 --- 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 @@ -23,6 +23,7 @@ import java.util.Map; @Data public class GpsGeofencingActionTestCase { + private EntityId entityId; private Map entityStates; private boolean msgInside; 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 index fe78f57f4e..5cd7e02c59 100644 --- 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 @@ -56,6 +56,7 @@ import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCES @ExtendWith(MockitoExtension.class) class TbGpsGeofencingActionNodeTest extends AbstractRuleNodeUpgradeTest { + @Mock private TbContext ctx; @Mock From f7914a4f96e3d0dd183b84ea147d5774678aa731 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 5 Feb 2024 11:43:11 +0200 Subject: [PATCH 088/128] UI: Clear translate zh_CN - removed keys that do not translate --- .../assets/locale/locale.constant-zh_CN.json | 446 +----------------- 1 file changed, 1 insertion(+), 445 deletions(-) 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 10db674ea3..22c8073c37 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4276,426 +4276,6 @@ "displayTypePrefix": "显示实时/历史前缀", "preview": "预览" }, - "unit": { - "millimeter": "毫米", - "centimeter": "厘米", - "angstrom": "埃", - "nanometer": "纳米", - "micrometer": "微米", - "meter": "米", - "kilometer": "千米", - "inch": "英寸", - "foot": "英尺", - "yard": "码", - "mile": "英里", - "nautical-mile": "海里", - "astronomical-unit": "天文单位", - "reciprocal-metre": "倒数米", - "meter-per-meter": "米/米", - "steradian": "球面度", - "thou": "千分之一英寸", - "barleycorn": "麦粒", - "hand": "手", - "chain": "链", - "furlong": "英寻", - "league": "里", - "fathom": "英寻", - "cable": "缆", - "link": "连", - "rod": "竿", - "nanogram": "纳克", - "microgram": "微克", - "milligram": "毫克", - "gram": "克", - "kilogram": "千克", - "tonne": "公吨", - "ounce": "盎司", - "pound": "磅", - "stone": "英石", - "hundredweight-count": "百磅", - "short-tons": "短吨", - "dalton": "道尔顿", - "grain": "格令", - "drachm": "打兰", - "quarter": "夸特", - "slug": "斯勒格", - "carat": "克拉", - "cubic-millimeter": "立方毫米", - "cubic-centimeter": "立方厘米", - "cubic-meter": "立方米", - "cubic-kilometer": "立方千米", - "microliter": "微升", - "milliliter": "毫升", - "liter": "升", - "hectoliter": "公石", - "cubic-inch": "立方英寸", - "cubic-foot": "立方英尺", - "cubic-yard": "立方码", - "fluid-ounce": "液盎司", - "pint": "品脱", - "quart": "夸脱", - "gallon": "加仑", - "oil-barrels": "桶", - "cubic-meter-per-kilogram": "立方米/千克", - "gill": "吉尔", - "hogshead": "大桶", - "teaspoon": "茶匙", - "tablespoon": "汤匙", - "cup": "杯", - "celsius": "摄氏度", - "kelvin": "开尔文", - "rankine": "兰氏度", - "fahrenheit": "华氏度", - "percent": "百分比", - "meter-per-second": "米/秒", - "kilometer-per-hour": "千米/时", - "foot-per-second": "英尺/秒", - "mile-per-hour": "英里/时", - "knot": "节", - "millimeters-per-minute": "毫米/分钟", - "kilometer-per-hour-squared": "千米/二次方时", - "foot-per-second-squared": "英尺/二次方秒", - "pascal": "帕斯卡", - "kilopascal": "千帕", - "megapascal": "兆帕", - "gigapascal": "吉帕", - "millibar": "毫巴", - "bar": "巴", - "kilobar": "千巴", - "newton": "牛顿", - "newton-meter": "牛顿米", - "foot-pounds": "英尺磅", - "inch-pounds": "英寸磅", - "newton-per-meter": "牛顿/米", - "atmospheres": "大气压", - "pounds-per-square-inch": "磅/平方英寸", - "torr": "托", - "inches-of-mercury": "英寸汞柱", - "pascal-per-square-meter": "帕斯卡/平方米", - "pound-per-square-inch": "磅/平方英寸", - "newton-per-square-meter": "牛顿/平方米", - "kilogram-force-per-square-meter": "千克力/平方米", - "pascal-per-square-centimeter": "帕斯卡/平方厘米", - "ton-force-per-square-inch": "吨力/平方英寸", - "kilonewton-per-square-meter": "千牛/平方米", - "newton-per-square-millimeter": "牛顿/平方毫米", - "microjoule": "微焦", - "millijoule": "毫焦", - "joule": "焦", - "kilojoule": "千焦", - "megajoule": "兆焦", - "gigajoule": "吉焦", - "watt-hour": "瓦时", - "kilowatt-hour": "千瓦时", - "electron-volts": "电子伏", - "joules-per-coulomb": "焦耳/库仑", - "british-thermal-unit": "英国热单位", - "foot-pound": "英尺磅", - "calorie": "卡路里", - "small-calorie": "小卡", - "kilocalorie": "千卡", - "joule-per-kelvin": "焦耳/开", - "joule-per-kilogram-kelvin": "焦耳/千克·开", - "joule-per-kilogram": "焦耳/千克", - "watt-per-meter-kelvin": "瓦/米·开", - "joule-per-cubic-meter": "焦耳/立方米", - "therm": "热", - "electric-dipole-moment": "电偶极矩", - "magnetic-dipole-moment": "磁偶极矩", - "debye": "戴拜", - "coulomb-per-square-meter-per-volt": "库仑·平方米/伏", - "milliwatt": "毫瓦", - "microwatt": "微瓦", - "watt": "瓦", - "kilowatt": "千瓦", - "megawatt": "兆瓦", - "gigawatt": "吉瓦", - "metric-horsepower": "公制马力", - "milliwatt-per-square-centimeter": "毫瓦/平方厘米", - "watt-per-square-centimeter": "瓦/平方厘米", - "kilowatt-per-square-centimeter": "千瓦/平方厘米", - "milliwatt-per-square-meter": "毫瓦/平方米", - "watt-per-square-meter": "瓦/平方米", - "kilowatt-per-square-meter": "千瓦/平方米", - "watt-per-square-inch": "瓦/平方英寸", - "kilowatt-per-square-inch": "千瓦/平方英寸", - "horsepower": "马力", - "btu-per-hour": "英国热单位/时", - "coulomb": "库仑", - "millicoulomb": "毫库仑", - "microcoulomb": "微库仑", - "picocoulomb": "皮库仑", - "coulomb-per-meter": "库仑/米", - "coulomb-per-cubic-meter": "库仑/立方米", - "coulomb-per-square-meter": "库仑/平方米", - "square-millimeter": "平方毫米", - "square-centimeter": "平方厘米", - "square-meter": "平方米", - "hectare": "公顷", - "square-kilometer": "平方千米", - "square-inch": "平方英寸", - "square-foot": "平方英尺", - "square-yard": "平方码", - "acre": "英亩", - "square-mile": "平方英里", - "are": "公亩", - "barn": "bar", - "circular-inch": "圆英寸", - "milliampere-hour": "毫安时", - "milliampere-hour-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 毫安时, 毫安时, mAh", - "ampere-hours": "安时", - "ampere-hours-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 安培, 安时, Ah", - "kiloampere-hours": "千安时", - "kiloampere-hours-tags": "电流, 电流流动, 电荷, 电流容量, 电流, 电气流, 千安时, 千安时, kAh", - "nanoampere": "纳安培", - "nanoampere-tags": "电流, 安培, 纳安培, nA", - "picoampere": "皮安培", - "picoampere-tags": "电流, 安培, 皮安培, pA", - "microampere": "微安培", - "microampere-tags": "电流, 微安培, 微安培, μA", - "milliampere": "毫安培", - "milliampere-tags": "电流, 毫安培, 毫安培, mA", - "ampere": "安培", - "ampere-tags": "电流, 电流流动, 电流, 电气流, 安培, 安培, 电流, A", - "kiloamperes": "千安培", - "kiloamperes-tags": "电流, 电流流动, 千安培, kA", - "microampere-per-square-centimeter": "微安培/平方厘米", - "microampere-per-square-centimeter-tags": "电流密度, 每平方厘米的微安培, µA/cm²", - "ampere-per-square-meter": "安培/平方米", - "ampere-per-square-meter-tags": "电流密度, 单位面积电流, 每平方米的安培, A/m²", - "ampere-per-meter": "安培/米", - "ampere-per-meter-tags": "磁场强度, 磁场强度, 每米的安培, A/m", - "oersted": "厄斯特德", - "oersted-tags": "磁场, 厄斯特德, Oe", - "bohr-magneton": "玻尔磁子", - "bohr-magneton-tags": "原子物理, 磁矩, 玻尔磁子, μB", - "ampere-meter-squared": "安培·平方米", - "ampere-meter-squared-tags": "磁矩, 偶极矩, 安培·平方米, A·m²", - "ampere-meter": "安培·米", - "ampere-meter-tags": "磁场, 电流环, 安培·米, A·m", - "nanovolt": "纳伏", - "picovolt": "皮伏", - "millivolts": "毫伏", - "microvolts": "微伏", - "volt": "伏", - "kilovolts": "千伏", - "dbmV": "分贝毫伏", - "dbm": "分贝毫瓦", - "volt-meter": "伏·米", - "kilovolt-meter": "千伏·米", - "megavolt-meter": "兆伏·米", - "microvolt-meter": "微伏·米", - "millivolt-meter": "毫伏·米", - "nanovolt-meter": "纳伏·米", - "ohm": "欧姆", - "microohm": "微欧", - "milliohm": "毫欧", - "kilohm": "千欧", - "megohm": "兆欧", - "gigohm": "吉欧", - "hertz": "赫兹", - "kilohertz": "千赫", - "megahertz": "兆赫", - "gigahertz": "吉赫", - "rpm": "转数/分钟", - "candela-per-square-meter": "坎德拉/平方米", - "candela": "坎德拉", - "lumen": "流明", - "lux": "勒克斯", - "foot-candle": "英尺烛光", - "lumen-per-square-meter": "流明/平方米", - "lux-second": "勒克斯·秒", - "lumen-second": "流明·秒", - "lumens-per-watt": "流明/瓦", - "absorbance": "吸光度", - "mole": "摩尔", - "nanomole": "纳摩尔", - "micromole": "微摩尔", - "millimole": "毫摩尔", - "kilomole": "千摩尔", - "mole-per-cubic-meter": "摩尔/立方米", - "rssi": "接收信号强度指示", - "ppm": "百万分之一", - "ppb": "十亿分之一", - "micrograms-per-cubic-meter": "微克/立方米", - "aqi": "空气质量指数", - "gram-per-cubic-meter": "克/立方米", - "gram-per-kilogram": "比湿", - "millimeters-per-second": "毫米/秒", - "neper": "纳珀", - "bel": "贝尔", - "decibel": "分贝", - "meters-per-second-squared": "平方米/秒", - "becquerel": "贝克勒", - "curie": "居里", - "gray": "戈瑞", - "sievert": "西弗特", - "roentgen": "伦琴", - "cps": "计数/秒", - "rad": "拉德", - "rem": "剂量当量", - "dps": "衰变/秒", - "rutherford": "卢瑟福", - "coulombs-per-kilogram": "库仑/千克", - "becquerels-per-cubic-meter": "贝克勒/立方米", - "curies-per-liter": "居里/升", - "becquerels-per-second": "贝克勒/秒", - "curies-per-second": "居里/秒", - "gy-per-second": "戈瑞/秒", - "watt-per-steradian": "瓦/立体弧度", - "watt-per-square-metre-steradian": "瓦/平方米·弧度", - "ph-level": "酸碱度", - "turbidity": "浊度", - "mg-per-liter": "毫克/升", - "microsiemens-per-centimeter": "微西门子/厘米", - "millisiemens-per-meter": "毫西门子/米", - "siemens-per-meter": "西门子/米", - "kilogram-per-cubic-meter": "千克/立方米", - "gram-per-cubic-centimeter": "克/立方厘米", - "kilogram-per-square-meter": "千克/平方米", - "milligram-per-milliliter": "毫克/毫升", - "milligram-per-cubic-meter": "毫克/立方米", - "pound-per-cubic-foot": "磅/立方英尺", - "ounces-per-cubic-inch": "盎司/立方英寸", - "tons-per-cubic-yard": "吨/立方码", - "particle-density": "颗粒密度", - "kilometers-per-liter": "千米/升", - "miles-per-gallon": "英里/加仑", - "liters-per-100-km": "升/100千米", - "gallons-per-mile": "加仑/英里", - "liters-per-hour": "升/小时", - "gallons-per-hour": "加仑/小时", - "beats-per-minute": "每分钟心跳数", - "millimeters-of-mercury": "毫米汞柱", - "milligrams-per-deciliter": "毫克/分升", - "g-force": "重力加速度", - "kilonewton": "千牛", - "kilogram-force": "千克力", - "pound-force": "磅力", - "kilopound-force": "千磅力", - "dyne": "达因", - "poundal": "磅力秒平方英尺", - "kip": "千磅力", - "gal": "加尔", - "gravity": "重力", - "hectopascal": "百帕", - "atmosphere": "大气压", - "millibars": "毫巴", - "inch-of-mercury": "英寸汞柱", - "richter-scale": "里氏震级", - "second": "秒", - "minute": "分钟", - "hour": "小时", - "day": "天", - "week": "周", - "month": "月", - "year": "年", - "cubic-foot-per-minute": "立方英尺/分钟", - "cubic-meters-per-hour": "立方米/小时", - "cubic-meters-per-second": "立方米/秒", - "liter-per-second": "升/秒", - "liter-per-minute": "升/分钟", - "gallons-per-minute": "加仑/分钟", - "cubic-foot-per-second": "立方英尺/秒", - "milliliters-per-minute": "毫升/分钟", - "bit": "位", - "byte": "字节", - "kilobyte": "千字节", - "megabyte": "兆字节", - "gigabyte": "吉字节", - "terabyte": "太字节", - "petabyte": "拍字节", - "exabyte": "艾字节", - "zettabyte": "泽字节", - "yottabyte": "尧字节", - "bit-per-second": "比特/秒", - "kilobit-per-second": "千比特/秒", - "megabit-per-second": "兆比特/秒", - "gigabit-per-second": "吉比特/秒", - "terabit-per-second": "太比特/秒", - "byte-per-second": "字节/秒", - "kilobyte-per-second": "千字节/秒", - "megabyte-per-second": "兆字节/秒", - "gigabyte-per-second": "吉字节/秒", - "degree": "度", - "radian": "弧度", - "gradian": "百分度", - "mil": "米尔", - "revolution": "圈", - "siemens": "西门子", - "millisiemens": "毫西门子", - "microsiemens": "微西门子", - "kilosiemens": "千西门子", - "megasiemens": "兆西门子", - "gigasiemens": "吉西门子", - "farad": "法拉", - "millifarad": "毫法拉", - "microfarad": "微法拉", - "nanofarad": "纳法拉", - "picofarad": "皮法拉", - "kilofarad": "千法拉", - "megafarad": "兆法拉", - "gigafarad": "吉法拉", - "terfarad": "太法拉", - "farad-per-meter": "法拉/米", - "tesla": "特斯拉", - "gauss": "高斯", - "kilogauss": "千高斯", - "millitesla": "毫特斯拉", - "microtesla": "微特斯拉", - "nanotesla": "纳特斯拉", - "kilotesla": "千特斯拉", - "megatesla": "兆特斯拉", - "millitesla-square-meters": "毫特斯拉平方米", - "gamma": "伽马", - "lambda": "兰姆达", - "square-meter-per-second": "平方米/秒", - "square-centimeter-per-second": "平方厘米/秒", - "stoke": "斯托克", - "centistokes": "厘斯托克", - "square-foot-per-second": "平方英尺/秒", - "square-inch-per-second": "平方英寸/秒", - "pascal-second": "帕斯卡秒", - "centipoise": "厘泊", - "poise": "泊", - "reynolds": "雷诺", - "pound-per-foot-hour": "磅/英尺-小时", - "newton-second-per-square-meter": "牛顿秒/平方米", - "dyne-second-per-square-centimeter": "达因秒/平方厘米", - "kilogram-per-meter-second": "千克/米·秒", - "tesla-square-meters": "特斯拉平方米", - "maxwell": "麦克斯韦", - "tesla-per-meter": "特斯拉/米", - "gauss-per-centimeter": "高斯/厘米", - "weber": "韦伯", - "microweber": "微韦伯", - "milliweber": "毫韦伯", - "gauss-square-centimeter": "高斯平方厘米", - "kilogauss-square-centimeter": "千高斯平方厘米", - "henry": "亨利", - "millihenry": "毫亨利", - "microhenry": "微亨利", - "nanohenry": "纳亨利", - "henry-per-meter": "亨利/米", - "tesla-meter-per-ampere": "特斯拉米/安", - "gauss-per-oersted": "高斯/厘斯特", - "kilogram-per-mole": "千克/摩尔", - "gram-per-mole": "克/摩尔", - "milligram-per-mole": "毫克/摩尔", - "joule-per-mole": "焦耳/摩尔", - "joule-per-mole-kelvin": "焦耳/摩尔·开尔文", - "millivolts-per-meter": "毫伏/米", - "volts-per-meter": "伏/米", - "kilovolts-per-meter": "千伏/米", - "radian-per-second": "弧度/秒", - "radian-per-second-squared": "弧度/二次方秒", - "revolutions-per-minute-per-second": "角加速度", - "revolutions-per-minute-per-second-squared": "角加速度", - "deg-per-second": "度/秒", - "degrees-brix": "白利糖度", - "katal": "卡塔尔", - "katal-per-cubic-metre": "卡塔尔/立方米" - }, "user": { "user": "用户", "users": "用户", @@ -6696,30 +6276,6 @@ "items-per-page-separator": "of" }, "language": { - "language": "语言", - "locales": { - "ca_ES": "Catalan", - "cs_CZ": "Česky", - "da_DK": "Dansk", - "de_DE": "Deutsch", - "el_GR": "Ελληνικά", - "en_US": "English", - "es_ES": "Español", - "fa_IR": "فارسي", - "fr_FR": "Français", - "it_IT": "Italiano", - "ja_JP": "日本語", - "ka_GE": "ქართული", - "ko_KR": "한국어", - "lv_LV": "Latviešu", - "nl_BE": "Koninkrijk België", - "pt_BR": "Português do Brasil", - "ro_RO": "Română", - "sl_SI": "Slovenščina", - "tr_TR": "Türkçe", - "uk_UA": "Українська", - "zh_CN": "简体中文", - "zh_TW": "繁體中文" - } + "language": "语言" } } From 567e6a493ee265d42e252abd0905f97e21a9f6dd Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 5 Feb 2024 12:57:22 +0200 Subject: [PATCH 089/128] UI: Refactoring after review --- .../components/vc/repository-settings.component.html | 2 +- ui-ngx/src/app/modules/home/home.component.ts | 10 +++++----- .../home/pages/account/account-routing.module.ts | 1 + .../modules/home/pages/admin/admin-routing.module.ts | 1 + .../home/pages/admin/oauth2-settings.component.ts | 5 ++--- .../home/pages/admin/security-settings.component.ts | 5 ++--- .../pages/admin/two-factor-auth-settings.component.ts | 5 ++--- ui-ngx/src/app/modules/home/pages/home-pages.models.ts | 4 ++-- .../pages/notification/notification-routing.module.ts | 1 - .../home/pages/widget/widget-library-routing.module.ts | 1 - ui-ngx/src/app/shared/components/page.component.ts | 5 ++++- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html index 7ba277364d..9b1c627ff2 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html @@ -28,7 +28,7 @@ -
+
diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index a314803e9c..0b41a90ae3 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -150,14 +150,14 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni this.textSearch.reset('', {emitEvent: false}); this.activeComponent = activeComponent; let showLoadingBar: boolean; - if (isDefined(this.activeComponent.activatedRoute?.data?.value?.showLoadingBar)) { - showLoadingBar = this.activeComponent.activatedRoute?.data?.value?.showLoadingBar; - } else if (isDefined(this.activeComponent?.showLoadingBar)) { - showLoadingBar = this.activeComponent.showLoadingBar; + if (isDefined(this.activeComponent.activatedRoute?.snapshot?.data?.showMainLoadingBar)) { + showLoadingBar = this.activeComponent.activatedRoute.snapshot.data.showMainLoadingBar; + } else if (isDefined(this.activeComponent?.showMainLoadingBar)) { + showLoadingBar = this.activeComponent.showMainLoadingBar; } if (activeComponent && activeComponent instanceof RouterTabsComponent) { this.hideLoadingBar = isDefinedAndNotNull(showLoadingBar) ? !showLoadingBar : true; - } else if (isDefinedAndNotNull(showLoadingBar)) { + } else if (activeComponent && activeComponent instanceof PageComponent && isDefinedAndNotNull(showLoadingBar)) { this.hideLoadingBar = !showLoadingBar; } if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { 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 798b07c461..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 @@ -54,14 +54,13 @@ import { TranslateService } from '@ngx-translate/core'; import { isDefined, isDefinedAndNotNull, randomAlphanumeric } from '@core/utils'; import { OAuth2Service } from '@core/http/oauth2.service'; import { ActivatedRoute } from '@angular/router'; -import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-oauth2-settings', templateUrl: './oauth2-settings.component.html', styleUrls: ['./oauth2-settings.component.scss', './settings-card.scss'] }) -export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, HasShowLoading, OnDestroy { +export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { constructor(protected store: Store, private route: ActivatedRoute, @@ -115,7 +114,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha templateProvider = ['Custom']; - showLoadingBar = false; + showMainLoadingBar = false; private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl; 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 f9449eba33..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 @@ -37,19 +37,18 @@ import { AuthService } from '@core/auth/auth.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; import { Observable, of } from 'rxjs'; -import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-security-settings', templateUrl: './security-settings.component.html', styleUrls: ['./security-settings.component.scss', './settings-card.scss'] }) -export class SecuritySettingsComponent extends PageComponent implements HasConfirmForm, HasShowLoading { +export class SecuritySettingsComponent extends PageComponent implements HasConfirmForm { securitySettingsFormGroup: UntypedFormGroup; jwtSecuritySettingsFormGroup: UntypedFormGroup; - showLoadingBar = false; + 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 208d133e3e..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 @@ -32,14 +32,13 @@ import { isNotEmptyStr } from '@core/utils'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { MatExpansionPanel } from '@angular/material/expansion'; -import { HasShowLoading } from '@home/pages/home-pages.models'; @Component({ selector: 'tb-2fa-settings', templateUrl: './two-factor-auth-settings.component.html', styleUrls: [ './settings-card.scss', './two-factor-auth-settings.component.scss'] }) -export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, HasShowLoading, OnDestroy { +export class TwoFactorAuthSettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { private readonly destroy$ = new Subject(); private readonly posIntValidation = [Validators.required, Validators.min(1), Validators.pattern(/^\d*$/)]; @@ -48,7 +47,7 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI twoFactorAuthProviderType = TwoFactorAuthProviderType; twoFactorAuthProvidersData = twoFactorAuthProvidersData; - showLoadingBar = false; + showMainLoadingBar = false; @ViewChildren(MatExpansionPanel) expansionPanel: QueryList; 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 456bbb6999..d2b7e9782c 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 @@ -31,7 +31,7 @@ export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction; loadingSubscription: Subscription; disabledOnLoadFormControls: Array = []; + showMainLoadingBar = true; + protected constructor(protected store: Store) { this.isLoading$ = this.store.pipe(delay(0), select(selectIsLoading), share()); } From f46aed1b8e1fce42426c6cf10b0c83b7879bcd41 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Mon, 5 Feb 2024 14:38:00 +0200 Subject: [PATCH 090/128] UI: fixed gridster options update for mobile mode --- .../home/components/dashboard/dashboard.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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..95b03a55ee 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 @@ -290,12 +290,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(); } From 8262be6e6451673e4ebd35988585193a82dcb871 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 5 Feb 2024 15:10:37 +0200 Subject: [PATCH 091/128] UI: Single switch widget improvements: Implement disabled state. Improve auto-scale. --- .../single-switch-basic-config.component.html | 11 ++++ .../single-switch-basic-config.component.ts | 2 + .../rpc/single-switch-widget.component.html | 6 +-- .../rpc/single-switch-widget.component.scss | 8 +-- .../lib/rpc/single-switch-widget.component.ts | 54 +++++++++++++++++-- .../lib/rpc/single-switch-widget.models.ts | 17 ++++++ ...ngle-switch-widget-settings.component.html | 11 ++++ ...single-switch-widget-settings.component.ts | 3 +- .../assets/locale/locale.constant-en_US.json | 5 +- 9 files changed, 105 insertions(+), 12 deletions(-) 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 4ac5934e93..df495e32df 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 @@ -52,6 +52,17 @@ [widgetType]="widgetType" formControlName="offUpdateState">
+
+
widgets.rpc-state.disabled-state
+ +
widget-config.appearance
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 82c2766a23..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 @@ -70,6 +70,7 @@ export class SingleSwitchBasicConfigComponent 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 SingleSwitchBasicConfigComponent 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/lib/rpc/single-switch-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.html index 5f085ff7b5..879902a3ef 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,8 +15,8 @@ limitations under the License. --> -
-
+
+
@@ -27,7 +27,7 @@
{{ offLabel }}
- +
{{ 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 92c8f87f09..bb5648a16d 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,15 +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; + inset: 12px; } .tb-single-switch-progress { 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 1a7274c5b6..1103afde39 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 @@ -45,6 +45,7 @@ import { DomSanitizer } from '@angular/platform-browser'; import { ValueType } from '@shared/models/constants'; import { UtilsService } from '@core/services/utils.service'; +const initialSwitchHeight = 60; const horizontalLayoutPadding = 48; const verticalLayoutPadding = 36; @@ -73,8 +74,10 @@ export class SingleSwitchWidgetComponent extends backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + overlayInset = '12px'; value = false; + disabled = false; layout: SingleSwitchLayout; @@ -159,6 +162,12 @@ export class SingleSwitchWidgetComponent extends 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); @@ -216,20 +225,59 @@ export class SingleSwitchWidgetComponent extends this.cd.markForCheck(); } + private onDisabled(value: boolean): void { + this.disabled = !!value; + if (this.disabled) { + if (this.showLabel) { + this.labelStyle = {...this.labelStyle, color: 'rgba(0, 0, 0, 0.38)'}; + } + if (this.showIcon) { + this.iconStyle = {...this.iconStyle, color: 'rgba(0, 0, 0, 0.38)'}; + } + if (this.showOnLabel) { + this.onLabelStyle = {...this.onLabelStyle, color: 'rgba(0, 0, 0, 0.38)'}; + } + if (this.showOffLabel) { + this.offLabelStyle = {...this.offLabelStyle, color: 'rgba(0, 0, 0, 0.38)'}; + } + } else { + if (this.showLabel) { + this.labelStyle = {...this.labelStyle, color: this.settings.labelColor}; + } + if (this.showIcon) { + this.iconStyle = {...this.iconStyle, color: this.settings.iconColor}; + } + if (this.showOnLabel) { + this.onLabelStyle = {...this.onLabelStyle, color: this.settings.onLabelColor}; + } + if (this.showOffLabel) { + this.offLabelStyle = {...this.offLabelStyle, color: this.settings.offLabelColor}; + } + } + 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 4f0401b3bd..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 @@ -51,6 +51,7 @@ export const singleSwitchLayoutImages = new Map( export interface SingleSwitchWidgetSettings { initialState: GetValueSettings; + disabledState: GetValueSettings; onUpdateState: SetValueSettings; offUpdateState: SetValueSettings; layout: SingleSwitchLayout; @@ -104,6 +105,22 @@ export const singleSwitchDefaultSettings: SingleSwitchWidgetSettings = { 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: { 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 9bdca64bbd..b498116317 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 @@ -51,6 +51,17 @@ [widgetType]="widgetType" formControlName="offUpdateState">
+
+
widgets.rpc-state.disabled-state
+ +
widget-config.card-style
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 8c7282e32b..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 @@ -69,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, []], @@ -109,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; 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 c9108e9bac..ae3bc28c31 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6522,12 +6522,15 @@ "rpc-state": { "initial-state": "Initial state", "initial-state-hint": "Action to get the initial value of the component.", + "disabled-state": "Disabled state", + "disabled-state-hint": "Condition under which the component is disabled.", "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" + "off": "Off", + "disabled": "Disabled" }, "value-action": { "do-nothing": "Do nothing", From 599911546b422355d92de1b2f03e4f7a35e2dafe Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 5 Feb 2024 15:23:30 +0200 Subject: [PATCH 092/128] UI: Widget button style fixes. --- .../button/widget-button-custom-style-panel.component.ts | 2 +- .../common/button/widget-button-custom-style.component.ts | 3 ++- .../app/shared/components/button/widget-button.component.scss | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) 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 index 419ce7e4d4..e6af6f2ab9 100644 --- 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 @@ -164,7 +164,7 @@ export class WidgetButtonCustomStylePanelComponent extends PageComponent impleme } private updatePreviewAppearance() { - this.previewAppearance = {...this.appearance}; + 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.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-custom-style.component.ts index b9a1605b19..a67f9a45ab 100644 --- 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 @@ -37,6 +37,7 @@ 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', @@ -147,7 +148,7 @@ export class WidgetButtonCustomStyleComponent implements OnInit, OnChanges, Cont } private updatePreviewAppearance() { - this.previewAppearance = {...this.appearance}; + this.previewAppearance = deepClone(this.appearance); if (this.modelValue) { this.previewAppearance.customStyle[this.state] = this.modelValue; } 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 index 546a3bed19..a0abc95731 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.component.scss +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.scss @@ -44,7 +44,7 @@ $mainColorActivatedFilled: var(--tb-widget-button-main-color-activated-filled, # $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-activated, $defaultBoxShadowColor); +$boxShadowColorDisabled: var(--tb-widget-button-box-shadow-color-disabled, $defaultBoxShadowColor); @mixin _tb-widget-button-styles($main, $background, $boxShadow) { From a8a10fefda0043e789e542c29b81b3bda6e244d9 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 5 Feb 2024 16:45:26 +0200 Subject: [PATCH 093/128] UI: Update buttons widget bundle image. Update single switch widget preview image. --- .../src/main/data/json/system/widget_bundles/buttons.json | 2 +- .../src/main/data/json/system/widget_types/single_switch.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/buttons.json b/application/src/main/data/json/system/widget_bundles/buttons.json index 046e16dd7e..b5f728dc25 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -2,7 +2,7 @@ "widgetsBundle": { "alias": "buttons", "title": "Buttons", - "image": null, + "image": "tb-image:YnV0dG9ucy5zdmc=:IkJ1dHRvbnMiIHN5c3RlbSBidW5kbGUgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NjMgNTEuMzMzM1Y0NC4zMzMzSDY2LjgzM1Y1MS4zMzMzSDcyLjY2NjNWNDJINzYuMTY2M0w2NC40OTk3IDMxLjVMNTIuODMzIDQySDU2LjMzM1Y1MS4zMzMzSDYyLjE2NjNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDQyLjkzMTZIODYuMjFMODYuMTg4NSA0MC45NjU4SDg5LjY2ODlDOTAuMjU2MiA0MC45NjU4IDkwLjc1MzkgNDAuODc5OSA5MS4xNjIxIDQwLjcwOEM5MS41Nzc1IDQwLjUyOSA5MS44OTI2IDQwLjI3NDcgOTIuMTA3NCAzOS45NDUzQzkyLjMyMjMgMzkuNjA4NyA5Mi40Mjk3IDM5LjIwNDEgOTIuNDI5NyAzOC43MzE0QzkyLjQyOTcgMzguMjA4NyA5Mi4zMjk0IDM3Ljc4MjYgOTIuMTI4OSAzNy40NTMxQzkxLjkyODQgMzcuMTIzNyA5MS42MjA0IDM2Ljg4MzggOTEuMjA1MSAzNi43MzM0QzkwLjc5NjkgMzYuNTgzIDkwLjI3NDEgMzYuNTA3OCA4OS42MzY3IDM2LjUwNzhIODcuMDI2NFY1MEg4NC4zMzAxVjM0LjM1OTRIODkuNjM2N0M5MC40OTYxIDM0LjM1OTQgOTEuMjYyNCAzNC40NDE3IDkxLjkzNTUgMzQuNjA2NEM5Mi42MTU5IDM0Ljc3MTIgOTMuMTkyNCAzNS4wMjkgOTMuNjY1IDM1LjM3OTlDOTQuMTQ0OSAzNS43MjM2IDk0LjUwNjUgMzYuMTYwNSA5NC43NSAzNi42OTA0Qzk1LjAwMDcgMzcuMjIwNCA5NS4xMjYgMzcuODUwNiA5NS4xMjYgMzguNTgxMUM5NS4xMjYgMzkuMjI1NiA5NC45NzIgMzkuODE2NCA5NC42NjQxIDQwLjM1MzVDOTQuMzU2MSA0MC44ODM1IDkzLjkwMTQgNDEuMzE2NyA5My4yOTk4IDQxLjY1MzNDOTIuNjk4MiA0MS45ODk5IDkxLjk0OTkgNDIuMTkwNCA5MS4wNTQ3IDQyLjI1NDlMOTAuMTk1MyA0Mi45MzE2Wk05MC4wNzcxIDUwSDg1LjM2MTNMODYuNTc1MiA0Ny44NjIzSDkwLjA3NzFDOTAuNjg1OSA0Ny44NjIzIDkxLjE5NDMgNDcuNzYyIDkxLjYwMjUgNDcuNTYxNUM5Mi4wMTA3IDQ3LjM1MzggOTIuMzE1MSA0Ny4wNzEgOTIuNTE1NiA0Ni43MTI5QzkyLjcyMzMgNDYuMzQ3NyA5Mi44MjcxIDQ1LjkyMTUgOTIuODI3MSA0NS40MzQ2QzkyLjgyNzEgNDQuOTI2MSA5Mi43Mzc2IDQ0LjQ4NTcgOTIuNTU4NiA0NC4xMTMzQzkyLjM3OTYgNDMuNzMzNyA5Mi4wOTY3IDQzLjQ0MzcgOTEuNzEgNDMuMjQzMkM5MS4zMjMyIDQzLjAzNTUgOTAuODE4NCA0Mi45MzE2IDkwLjE5NTMgNDIuOTMxNkg4Ny4xNjZMODcuMTg3NSA0MC45NjU4SDkxLjEyOTlMOTEuNzQyMiA0MS43MDdDOTIuNjAxNiA0MS43MzU3IDkzLjMwNyA0MS45MjU1IDkzLjg1ODQgNDIuMjc2NEM5NC40MTcgNDIuNjI3MyA5NC44MzI0IDQzLjA4MiA5NS4xMDQ1IDQzLjY0MDZDOTUuMzc2NiA0NC4xOTkyIDk1LjUxMjcgNDQuODAwOCA5NS41MTI3IDQ1LjQ0NTNDOTUuNTEyNyA0Ni40NDA4IDk1LjI5NDMgNDcuMjc1MSA5NC44NTc0IDQ3Ljk0ODJDOTQuNDI3NyA0OC42MjE0IDkzLjgwODMgNDkuMTMzNSA5Mi45OTkgNDkuNDg0NEM5Mi4xODk4IDQ5LjgyODEgOTEuMjE1OCA1MCA5MC4wNzcxIDUwWk0xMDUuMjE2IDQ3LjI2MDdWMzguMzc3SDEwNy44MTVWNTBIMTA1LjM2NkwxMDUuMjE2IDQ3LjI2MDdaTTEwNS41ODEgNDQuODQzOEwxMDYuNDUxIDQ0LjgyMjNDMTA2LjQ1MSA0NS42MDI5IDEwNi4zNjUgNDYuMzIyNiAxMDYuMTkzIDQ2Ljk4MTRDMTA2LjAyMSA0Ny42MzMxIDEwNS43NTcgNDguMjAyNSAxMDUuMzk4IDQ4LjY4OTVDMTA1LjA0IDQ5LjE2OTMgMTA0LjU4MiA0OS41NDUyIDEwNC4wMjMgNDkuODE3NEMxMDMuNDY1IDUwLjA4MjQgMTAyLjc5NSA1MC4yMTQ4IDEwMi4wMTUgNTAuMjE0OEMxMDEuNDQ5IDUwLjIxNDggMTAwLjkzIDUwLjEzMjUgMTAwLjQ1NyA0OS45Njc4Qzk5Ljk4NDQgNDkuODAzMSA5OS41NzYyIDQ5LjU0ODggOTkuMjMyNCA0OS4yMDUxQzk4Ljg5NTggNDguODYxMyA5OC42MzQ0IDQ4LjQxMzcgOTguNDQ4MiA0Ny44NjIzQzk4LjI2MiA0Ny4zMTA5IDk4LjE2ODkgNDYuNjUyIDk4LjE2ODkgNDUuODg1N1YzOC4zNzdIMTAwLjc1OFY0NS45MDcyQzEwMC43NTggNDYuMzI5OCAxMDAuODA4IDQ2LjY4NDIgMTAwLjkwOCA0Ni45NzA3QzEwMS4wMDggNDcuMjUgMTAxLjE0NSA0Ny40NzU2IDEwMS4zMTYgNDcuNjQ3NUMxMDEuNDg4IDQ3LjgxOTMgMTAxLjY4OSA0Ny45NDExIDEwMS45MTggNDguMDEyN0MxMDIuMTQ3IDQ4LjA4NDMgMTAyLjM5MSA0OC4xMjAxIDEwMi42NDggNDguMTIwMUMxMDMuMzg2IDQ4LjEyMDEgMTAzLjk2NiA0Ny45NzY5IDEwNC4zODkgNDcuNjkwNEMxMDQuODE4IDQ3LjM5NjggMTA1LjEyMyA0Ny4wMDI5IDEwNS4zMDIgNDYuNTA4OEMxMDUuNDg4IDQ2LjAxNDYgMTA1LjU4MSA0NS40NTk2IDEwNS41ODEgNDQuODQzOFpNMTE2LjA0NyAzOC4zNzdWNDAuMjY3NkgxMDkuNDk0VjM4LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDM1LjUzMDNIMTEzLjk3NFY0Ni43ODgxQzExMy45NzQgNDcuMTQ2MiAxMTQuMDI0IDQ3LjQyMTkgMTE0LjEyNCA0Ny42MTUyQzExNC4yMzEgNDcuODAxNCAxMTQuMzc4IDQ3LjkyNjggMTE0LjU2NCA0Ny45OTEyQzExNC43NTEgNDguMDU1NyAxMTQuOTY5IDQ4LjA4NzkgMTE1LjIyIDQ4LjA4NzlDMTE1LjM5OSA0OC4wODc5IDExNS41NzEgNDguMDc3MSAxMTUuNzM1IDQ4LjA1NTdDMTE1LjkgNDguMDM0MiAxMTYuMDMzIDQ4LjAxMjcgMTE2LjEzMyA0Ny45OTEyTDExNi4xNDQgNDkuOTY3OEMxMTUuOTI5IDUwLjAzMjIgMTE1LjY3OCA1MC4wODk1IDExNS4zOTIgNTAuMTM5NkMxMTUuMTEyIDUwLjE4OTggMTE0Ljc5IDUwLjIxNDggMTE0LjQyNSA1MC4yMTQ4QzExMy44MyA1MC4yMTQ4IDExMy4zMDQgNTAuMTExIDExMi44NDYgNDkuOTAzM0MxMTIuMzg3IDQ5LjY4ODUgMTEyLjAyOSA0OS4zNDExIDExMS43NzEgNDguODYxM0MxMTEuNTE0IDQ4LjM4MTUgMTExLjM4NSA0Ny43NDQxIDExMS4zODUgNDYuOTQ5MlYzNS41MzAzWk0xMjMuNjIzIDM4LjM3N1Y0MC4yNjc2SDExNy4wN1YzOC4zNzdIMTIzLjYyM1pNMTE4Ljk2MSAzNS41MzAzSDEyMS41NVY0Ni43ODgxQzEyMS41NSA0Ny4xNDYyIDEyMS42IDQ3LjQyMTkgMTIxLjcgNDcuNjE1MkMxMjEuODA4IDQ3LjgwMTQgMTIxLjk1NCA0Ny45MjY4IDEyMi4xNDEgNDcuOTkxMkMxMjIuMzI3IDQ4LjA1NTcgMTIyLjU0NSA0OC4wODc5IDEyMi43OTYgNDguMDg3OUMxMjIuOTc1IDQ4LjA4NzkgMTIzLjE0NyA0OC4wNzcxIDEyMy4zMTIgNDguMDU1N0MxMjMuNDc2IDQ4LjAzNDIgMTIzLjYwOSA0OC4wMTI3IDEyMy43MDkgNDcuOTkxMkwxMjMuNzIgNDkuOTY3OEMxMjMuNTA1IDUwLjAzMjIgMTIzLjI1NCA1MC4wODk1IDEyMi45NjggNTAuMTM5NkMxMjIuNjg4IDUwLjE4OTggMTIyLjM2NiA1MC4yMTQ4IDEyMi4wMDEgNTAuMjE0OEMxMjEuNDA3IDUwLjIxNDggMTIwLjg4IDUwLjExMSAxMjAuNDIyIDQ5LjkwMzNDMTE5Ljk2NCA0OS42ODg1IDExOS42MDUgNDkuMzQxMSAxMTkuMzQ4IDQ4Ljg2MTNDMTE5LjA5IDQ4LjM4MTUgMTE4Ljk2MSA0Ny43NDQxIDExOC45NjEgNDYuOTQ5MlYzNS41MzAzWk0xMjUuMTE5IDQ0LjMxNzRWNDQuMDcwM0MxMjUuMTE5IDQzLjIzMjQgMTI1LjI0MSA0Mi40NTU0IDEyNS40ODQgNDEuNzM5M0MxMjUuNzI4IDQxLjAxNiAxMjYuMDc5IDQwLjM4OTMgMTI2LjUzNyAzOS44NTk0QzEyNy4wMDMgMzkuMzIyMyAxMjcuNTY4IDM4LjkwNjkgMTI4LjIzNCAzOC42MTMzQzEyOC45MDggMzguMzEyNSAxMjkuNjY3IDM4LjE2MjEgMTMwLjUxMiAzOC4xNjIxQzEzMS4zNjQgMzguMTYyMSAxMzIuMTIzIDM4LjMxMjUgMTMyLjc4OSAzOC42MTMzQzEzMy40NjIgMzguOTA2OSAxMzQuMDMyIDM5LjMyMjMgMTM0LjQ5NyAzOS44NTk0QzEzNC45NjMgNDAuMzg5MyAxMzUuMzE3IDQxLjAxNiAxMzUuNTYxIDQxLjczOTNDMTM1LjgwNCA0Mi40NTU0IDEzNS45MjYgNDMuMjMyNCAxMzUuOTI2IDQ0LjA3MDNWNDQuMzE3NEMxMzUuOTI2IDQ1LjE1NTMgMTM1LjgwNCA0NS45MzIzIDEzNS41NjEgNDYuNjQ4NEMxMzUuMzE3IDQ3LjM2NDYgMTM0Ljk2MyA0Ny45OTEyIDEzNC40OTcgNDguNTI4M0MxMzQuMDMyIDQ5LjA1ODMgMTMzLjQ2NiA0OS40NzM2IDEzMi44IDQ5Ljc3NDRDMTMyLjEzNCA1MC4wNjggMTMxLjM3OCA1MC4yMTQ4IDEzMC41MzMgNTAuMjE0OEMxMjkuNjgxIDUwLjIxNDggMTI4LjkxOCA1MC4wNjggMTI4LjI0NSA0OS43NzQ0QzEyNy41NzkgNDkuNDczNiAxMjcuMDEzIDQ5LjA1ODMgMTI2LjU0OCA0OC41MjgzQzEyNi4wODIgNDcuOTkxMiAxMjUuNzI4IDQ3LjM2NDYgMTI1LjQ4NCA0Ni42NDg0QzEyNS4yNDEgNDUuOTMyMyAxMjUuMTE5IDQ1LjE1NTMgMTI1LjExOSA0NC4zMTc0Wk0xMjcuNzA4IDQ0LjA3MDNWNDQuMzE3NEMxMjcuNzA4IDQ0Ljg0MDIgMTI3Ljc2MiA0NS4zMzQzIDEyNy44NjkgNDUuNzk5OEMxMjcuOTc3IDQ2LjI2NTMgMTI4LjE0NSA0Ni42NzM1IDEyOC4zNzQgNDcuMDI0NEMxMjguNjAzIDQ3LjM3NTMgMTI4Ljg5NyA0Ny42NTEgMTI5LjI1NSA0Ny44NTE2QzEyOS42MTMgNDguMDUyMSAxMzAuMDM5IDQ4LjE1MjMgMTMwLjUzMyA0OC4xNTIzQzEzMS4wMTMgNDguMTUyMyAxMzEuNDI4IDQ4LjA1MjEgMTMxLjc3OSA0Ny44NTE2QzEzMi4xMzcgNDcuNjUxIDEzMi40MzEgNDcuMzc1MyAxMzIuNjYgNDcuMDI0NEMxMzIuODg5IDQ2LjY3MzUgMTMzLjA1OCA0Ni4yNjUzIDEzMy4xNjUgNDUuNzk5OEMxMzMuMjggNDUuMzM0MyAxMzMuMzM3IDQ0Ljg0MDIgMTMzLjMzNyA0NC4zMTc0VjQ0LjA3MDNDMTMzLjMzNyA0My41NTQ3IDEzMy4yOCA0My4wNjc3IDEzMy4xNjUgNDIuNjA5NEMxMzMuMDU4IDQyLjE0MzkgMTMyLjg4NiA0MS43MzIxIDEzMi42NDkgNDEuMzc0QzEzMi40MiA0MS4wMTYgMTMyLjEyNyA0MC43MzY3IDEzMS43NjkgNDAuNTM2MUMxMzEuNDE4IDQwLjMyODUgMTMwLjk5OSA0MC4yMjQ2IDEzMC41MTIgNDAuMjI0NkMxMzAuMDI1IDQwLjIyNDYgMTI5LjYwMiA0MC4zMjg1IDEyOS4yNDQgNDAuNTM2MUMxMjguODkzIDQwLjczNjcgMTI4LjYwMyA0MS4wMTYgMTI4LjM3NCA0MS4zNzRDMTI4LjE0NSA0MS43MzIxIDEyNy45NzcgNDIuMTQzOSAxMjcuODY5IDQyLjYwOTRDMTI3Ljc2MiA0My4wNjc3IDEyNy43MDggNDMuNTU0NyAxMjcuNzA4IDQ0LjA3MDNaTTE0MC45MTMgNDAuODU4NFY1MEgxMzguMzI0VjM4LjM3N0gxNDAuNzYzTDE0MC45MTMgNDAuODU4NFpNMTQwLjQ1MSA0My43NTg4TDEzOS42MTMgNDMuNzQ4QzEzOS42MiA0Mi45MjQ1IDEzOS43MzUgNDIuMTY4OSAxMzkuOTU3IDQxLjQ4MTRDMTQwLjE4NiA0MC43OTM5IDE0MC41MDEgNDAuMjAzMSAxNDAuOTAyIDM5LjcwOUMxNDEuMzExIDM5LjIxNDggMTQxLjc5OCAzOC44MzUzIDE0Mi4zNjMgMzguNTcwM0MxNDIuOTI5IDM4LjI5ODIgMTQzLjU1OSAzOC4xNjIxIDE0NC4yNTQgMzguMTYyMUMxNDQuODEyIDM4LjE2MjEgMTQ1LjMxNyAzOC4yNDA5IDE0NS43NjkgMzguMzk4NEMxNDYuMjI3IDM4LjU0ODggMTQ2LjYxNyAzOC43OTU5IDE0Ni45MzkgMzkuMTM5NkMxNDcuMjY5IDM5LjQ4MzQgMTQ3LjUyIDM5LjkzMSAxNDcuNjkxIDQwLjQ4MjRDMTQ3Ljg2MyA0MS4wMjY3IDE0Ny45NDkgNDEuNjk2MyAxNDcuOTQ5IDQyLjQ5MTJWNTBIMTQ1LjM1VjQyLjQ4MDVDMTQ1LjM1IDQxLjkyMTkgMTQ1LjI2NyA0MS40ODE0IDE0NS4xMDMgNDEuMTU5MkMxNDQuOTQ1IDQwLjgyOTggMTQ0LjcxMiA0MC41OTcgMTQ0LjQwNCA0MC40NjA5QzE0NC4xMDQgNDAuMzE3NyAxNDMuNzI4IDQwLjI0NjEgMTQzLjI3NiA0MC4yNDYxQzE0Mi44MzIgNDAuMjQ2MSAxNDIuNDM1IDQwLjMzOTIgMTQyLjA4NCA0MC41MjU0QzE0MS43MzMgNDAuNzExNiAxNDEuNDM2IDQwLjk2NTggMTQxLjE5MiA0MS4yODgxQzE0MC45NTYgNDEuNjEwNCAxNDAuNzczIDQxLjk4MjcgMTQwLjY0NSA0Mi40MDUzQzE0MC41MTYgNDIuODI3OCAxNDAuNDUxIDQzLjI3OSAxNDAuNDUxIDQzLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgMTExVjExMy4zMzNINzUuNTIxM0w2NC4zMzMgMTI0LjUyMkw2NS45NzggMTI2LjE2N0w3Ny4xNjYzIDExNC45NzhWMTI1SDc5LjQ5OTdWMTExSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDEyMS45NzJDMTAwLjU2NCAxMjEuNjQ5IDEwMC41MTQgMTIxLjM2MyAxMDAuNDE0IDEyMS4xMTJDMTAwLjMyMSAxMjAuODYyIDEwMC4xNTMgMTIwLjYzMiA5OS45MDkyIDEyMC40MjVDOTkuNjY1NyAxMjAuMjE3IDk5LjMyMTkgMTIwLjAxNyA5OC44Nzc5IDExOS44MjNDOTguNDQxMSAxMTkuNjIzIDk3Ljg4MjUgMTE5LjQxOSA5Ny4yMDIxIDExOS4yMTFDOTYuNDU3NCAxMTguOTgyIDk1Ljc2OTkgMTE4LjcyOCA5NS4xMzk2IDExOC40NDhDOTQuNTE2NiAxMTguMTYyIDkzLjk3MjMgMTE3LjgzMiA5My41MDY4IDExNy40NkM5My4wNDEzIDExNy4wOCA5Mi42Nzk3IDExNi42NDcgOTIuNDIxOSAxMTYuMTZDOTIuMTY0MSAxMTUuNjY2IDkyLjAzNTIgMTE1LjA5NyA5Mi4wMzUyIDExNC40NTJDOTIuMDM1MiAxMTMuODE1IDkyLjE2NzYgMTEzLjIzNSA5Mi40MzI2IDExMi43MTJDOTIuNzA0OCAxMTIuMTg5IDkzLjA4NzkgMTExLjczOCA5My41ODIgMTExLjM1OEM5NC4wODMzIDExMC45NzIgOTQuNjc0MiAxMTAuNjc0IDk1LjM1NDUgMTEwLjQ2N0M5Ni4wMzQ4IDExMC4yNTIgOTYuNzg2OCAxMTAuMTQ1IDk3LjYxMDQgMTEwLjE0NUM5OC43NzA1IDExMC4xNDUgOTkuNzY5NSAxMTAuMzU5IDEwMC42MDcgMTEwLjc4OUMxMDEuNDUyIDExMS4yMTkgMTAyLjEwMSAxMTEuNzk1IDEwMi41NTIgMTEyLjUxOUMxMDMuMDEgMTEzLjI0MiAxMDMuMjM5IDExNC4wNCAxMDMuMjM5IDExNC45MTRIMTAwLjU2NEMxMDAuNTY0IDExNC4zOTggMTAwLjQ1MyAxMTMuOTQ0IDEwMC4yMzEgMTEzLjU1QzEwMC4wMTcgMTEzLjE0OSA5OS42ODcyIDExMi44MzQgOTkuMjQzMiAxMTIuNjA0Qzk4LjgwNjMgMTEyLjM3NSA5OC4yNTEzIDExMi4yNjEgOTcuNTc4MSAxMTIuMjYxQzk2Ljk0MDggMTEyLjI2MSA5Ni40MTA4IDExMi4zNTcgOTUuOTg4MyAxMTIuNTUxQzk1LjU2NTggMTEyLjc0NCA5NS4yNTA3IDExMy4wMDYgOTUuMDQzIDExMy4zMzVDOTQuODM1MyAxMTMuNjY0IDk0LjczMTQgMTE0LjAzNyA5NC43MzE0IDExNC40NTJDOTQuNzMxNCAxMTQuNzQ2IDk0Ljc5OTUgMTE1LjAxNCA5NC45MzU1IDExNS4yNThDOTUuMDcxNiAxMTUuNDk0IDk1LjI3OTMgMTE1LjcxNiA5NS41NTg2IDExNS45MjRDOTUuODM3OSAxMTYuMTI0IDk2LjE4ODggMTE2LjMxNCA5Ni42MTEzIDExNi40OTNDOTcuMDMzOSAxMTYuNjcyIDk3LjUzMTYgMTE2Ljg0NCA5OC4xMDQ1IDExNy4wMDlDOTguOTcxIDExNy4yNjcgOTkuNzI2NiAxMTcuNTUzIDEwMC4zNzEgMTE3Ljg2OEMxMDEuMDE2IDExOC4xNzYgMTAxLjU1MyAxMTguNTI3IDEwMS45ODIgMTE4LjkyMUMxMDIuNDEyIDExOS4zMTUgMTAyLjczNCAxMTkuNzYyIDEwMi45NDkgMTIwLjI2NEMxMDMuMTY0IDEyMC43NTggMTAzLjI3MSAxMjEuMzIgMTAzLjI3MSAxMjEuOTVDMTAzLjI3MSAxMjIuNjA5IDEwMy4xMzkgMTIzLjIwMyAxMDIuODc0IDEyMy43MzNDMTAyLjYwOSAxMjQuMjU2IDEwMi4yMjkgMTI0LjcwNCAxMDEuNzM1IDEyNS4wNzZDMTAxLjI0OCAxMjUuNDQxIDEwMC42NjEgMTI1LjcyNCA5OS45NzM2IDEyNS45MjVDOTkuMjkzMyAxMjYuMTE4IDk4LjUzNDIgMTI2LjIxNSA5Ny42OTYzIDEyNi4yMTVDOTYuOTQ0MyAxMjYuMjE1IDk2LjIwMzEgMTI2LjExNSA5NS40NzI3IDEyNS45MTRDOTQuNzQ5MyAxMjUuNzE0IDk0LjA5MDUgMTI1LjQwOSA5My40OTYxIDEyNS4wMDFDOTIuOTAxNyAxMjQuNTg2IDkyLjQyOSAxMjQuMDcgOTIuMDc4MSAxMjMuNDU0QzkxLjcyNzIgMTIyLjgzMSA5MS41NTE4IDEyMi4xMDQgOTEuNTUxOCAxMjEuMjczSDk0LjI0OEM5NC4yNDggMTIxLjc4MiA5NC4zMzQgMTIyLjIxNSA5NC41MDU5IDEyMi41NzNDOTQuNjg0OSAxMjIuOTMxIDk0LjkzMiAxMjMuMjI1IDk1LjI0NzEgMTIzLjQ1NEM5NS41NjIyIDEyMy42NzYgOTUuOTI3NCAxMjMuODQxIDk2LjM0MjggMTIzLjk0OEM5Ni43NjUzIDEyNC4wNTYgOTcuMjE2NSAxMjQuMTA5IDk3LjY5NjMgMTI0LjEwOUM5OC4zMjY1IDEyNC4xMDkgOTguODUyOSAxMjQuMDIgOTkuMjc1NCAxMjMuODQxQzk5LjcwNTEgMTIzLjY2MiAxMDAuMDI3IDEyMy40MTEgMTAwLjI0MiAxMjMuMDg5QzEwMC40NTcgMTIyLjc2NyAxMDAuNTY0IDEyMi4zOTQgMTAwLjU2NCAxMjEuOTcyWk0xMTAuNzcyIDEyNi4yMTVDMTA5LjkxMyAxMjYuMjE1IDEwOS4xMzYgMTI2LjA3NSAxMDguNDQxIDEyNS43OTZDMTA3Ljc1NCAxMjUuNTA5IDEwNy4xNjcgMTI1LjExMiAxMDYuNjggMTI0LjYwNEMxMDYuMiAxMjQuMDk1IDEwNS44MzEgMTIzLjQ5NyAxMDUuNTczIDEyMi44MUMxMDUuMzE1IDEyMi4xMjIgMTA1LjE4NyAxMjEuMzgxIDEwNS4xODcgMTIwLjU4NlYxMjAuMTU2QzEwNS4xODcgMTE5LjI0NyAxMDUuMzE5IDExOC40MjMgMTA1LjU4NCAxMTcuNjg2QzEwNS44NDkgMTE2Ljk0OCAxMDYuMjE4IDExNi4zMTggMTA2LjY5IDExNS43OTVDMTA3LjE2MyAxMTUuMjY1IDEwNy43MjIgMTE0Ljg2IDEwOC4zNjYgMTE0LjU4MUMxMDkuMDExIDExNC4zMDIgMTA5LjcwOSAxMTQuMTYyIDExMC40NjEgMTE0LjE2MkMxMTEuMjkyIDExNC4xNjIgMTEyLjAxOSAxMTQuMzAyIDExMi42NDIgMTE0LjU4MUMxMTMuMjY1IDExNC44NiAxMTMuNzggMTE1LjI1NCAxMTQuMTg4IDExNS43NjNDMTE0LjYwNCAxMTYuMjY0IDExNC45MTIgMTE2Ljg2MiAxMTUuMTEyIDExNy41NTdDMTE1LjMyIDExOC4yNTEgMTE1LjQyNCAxMTkuMDE4IDExNS40MjQgMTE5Ljg1NVYxMjAuOTYySDEwNi40NDNWMTE5LjEwNEgxMTIuODY3VjExOC44OTlDMTEyLjg1MyAxMTguNDM0IDExMi43NiAxMTcuOTk3IDExMi41ODggMTE3LjU4OUMxMTIuNDIzIDExNy4xODEgMTEyLjE2OSAxMTYuODUxIDExMS44MjUgMTE2LjYwMUMxMTEuNDgxIDExNi4zNSAxMTEuMDIzIDExNi4yMjUgMTEwLjQ1IDExNi4yMjVDMTEwLjAyMSAxMTYuMjI1IDEwOS42MzcgMTE2LjMxOCAxMDkuMzAxIDExNi41MDRDMTA4Ljk3MSAxMTYuNjgzIDEwOC42OTYgMTE2Ljk0NCAxMDguNDc0IDExNy4yODhDMTA4LjI1MiAxMTcuNjMyIDEwOC4wOCAxMTguMDQ3IDEwNy45NTggMTE4LjUzNEMxMDcuODQzIDExOS4wMTQgMTA3Ljc4NiAxMTkuNTU1IDEwNy43ODYgMTIwLjE1NlYxMjAuNTg2QzEwNy43ODYgMTIxLjA5NCAxMDcuODU0IDEyMS41NjcgMTA3Ljk5IDEyMi4wMDRDMTA4LjEzMyAxMjIuNDM0IDEwOC4zNDEgMTIyLjgxIDEwOC42MTMgMTIzLjEzMkMxMDguODg1IDEyMy40NTQgMTA5LjIxNSAxMjMuNzA4IDEwOS42MDIgMTIzLjg5NUMxMDkuOTg4IDEyNC4wNzQgMTEwLjQyOSAxMjQuMTYzIDExMC45MjMgMTI0LjE2M0MxMTEuNTQ2IDEyNC4xNjMgMTEyLjEwMSAxMjQuMDM4IDExMi41ODggMTIzLjc4N0MxMTMuMDc1IDEyMy41MzYgMTEzLjQ5NyAxMjMuMTgyIDExMy44NTUgMTIyLjcyNEwxMTUuMjIgMTI0LjA0NUMxMTQuOTY5IDEyNC40MSAxMTQuNjQzIDEyNC43NjEgMTE0LjI0MiAxMjUuMDk4QzExMy44NDEgMTI1LjQyNyAxMTMuMzUxIDEyNS42OTYgMTEyLjc3MSAxMjUuOTAzQzExMi4xOTggMTI2LjExMSAxMTEuNTMyIDEyNi4yMTUgMTEwLjc3MiAxMjYuMjE1Wk0xMjAuMjYxIDExNi44NThWMTI2SDExNy42NzJWMTE0LjM3N0gxMjAuMTFMMTIwLjI2MSAxMTYuODU4Wk0xMTkuNzk5IDExOS43NTlMMTE4Ljk2MSAxMTkuNzQ4QzExOC45NjggMTE4LjkyNCAxMTkuMDgzIDExOC4xNjkgMTE5LjMwNSAxMTcuNDgxQzExOS41MzQgMTE2Ljc5NCAxMTkuODQ5IDExNi4yMDMgMTIwLjI1IDExNS43MDlDMTIwLjY1OCAxMTUuMjE1IDEyMS4xNDUgMTE0LjgzNSAxMjEuNzExIDExNC41N0MxMjIuMjc3IDExNC4yOTggMTIyLjkwNyAxMTQuMTYyIDEyMy42MDIgMTE0LjE2MkMxMjQuMTYgMTE0LjE2MiAxMjQuNjY1IDExNC4yNDEgMTI1LjExNiAxMTQuMzk4QzEyNS41NzUgMTE0LjU0OSAxMjUuOTY1IDExNC43OTYgMTI2LjI4NyAxMTUuMTRDMTI2LjYxNyAxMTUuNDgzIDEyNi44NjcgMTE1LjkzMSAxMjcuMDM5IDExNi40ODJDMTI3LjIxMSAxMTcuMDI3IDEyNy4yOTcgMTE3LjY5NiAxMjcuMjk3IDExOC40OTFWMTI2SDEyNC42OTdWMTE4LjQ4QzEyNC42OTcgMTE3LjkyMiAxMjQuNjE1IDExNy40ODEgMTI0LjQ1IDExNy4xNTlDMTI0LjI5MyAxMTYuODMgMTI0LjA2IDExNi41OTcgMTIzLjc1MiAxMTYuNDYxQzEyMy40NTEgMTE2LjMxOCAxMjMuMDc1IDExNi4yNDYgMTIyLjYyNCAxMTYuMjQ2QzEyMi4xOCAxMTYuMjQ2IDEyMS43ODMgMTE2LjMzOSAxMjEuNDMyIDExNi41MjVDMTIxLjA4MSAxMTYuNzEyIDEyMC43ODQgMTE2Ljk2NiAxMjAuNTQgMTE3LjI4OEMxMjAuMzA0IDExNy42MSAxMjAuMTIxIDExNy45ODMgMTE5Ljk5MiAxMTguNDA1QzExOS44NjMgMTE4LjgyOCAxMTkuNzk5IDExOS4yNzkgMTE5Ljc5OSAxMTkuNzU5Wk0xMzcuMjc5IDEyMy41OTRWMTA5LjVIMTM5Ljg3OVYxMjZIMTM3LjUyNkwxMzcuMjc5IDEyMy41OTRaTTEyOS43MTcgMTIwLjMxN1YxMjAuMDkyQzEyOS43MTcgMTE5LjIxMSAxMjkuODIxIDExOC40MDkgMTMwLjAyOCAxMTcuNjg2QzEzMC4yMzYgMTE2Ljk1NSAxMzAuNTM3IDExNi4zMjggMTMwLjkzMSAxMTUuODA2QzEzMS4zMjUgMTE1LjI3NiAxMzEuODA0IDExNC44NzEgMTMyLjM3IDExNC41OTJDMTMyLjkzNiAxMTQuMzA1IDEzMy41NzMgMTE0LjE2MiAxMzQuMjgyIDExNC4xNjJDMTM0Ljk4NCAxMTQuMTYyIDEzNS42IDExNC4yOTggMTM2LjEzIDExNC41N0MxMzYuNjYgMTE0Ljg0MiAxMzcuMTExIDExNS4yMzMgMTM3LjQ4MyAxMTUuNzQxQzEzNy44NTYgMTE2LjI0MyAxMzguMTUzIDExNi44NDQgMTM4LjM3NSAxMTcuNTQ2QzEzOC41OTcgMTE4LjI0MSAxMzguNzU1IDExOS4wMTQgMTM4Ljg0OCAxMTkuODY2VjEyMC41ODZDMTM4Ljc1NSAxMjEuNDE3IDEzOC41OTcgMTIyLjE3NiAxMzguMzc1IDEyMi44NjNDMTM4LjE1MyAxMjMuNTUxIDEzNy44NTYgMTI0LjE0NSAxMzcuNDgzIDEyNC42NDZDMTM3LjExMSAxMjUuMTQ4IDEzNi42NTYgMTI1LjUzNSAxMzYuMTE5IDEyNS44MDdDMTM1LjU4OSAxMjYuMDc5IDEzNC45NyAxMjYuMjE1IDEzNC4yNjEgMTI2LjIxNUMxMzMuNTU5IDEyNi4yMTUgMTMyLjkyNSAxMjYuMDY4IDEzMi4zNTkgMTI1Ljc3NEMxMzEuODAxIDEyNS40ODEgMTMxLjMyNSAxMjUuMDY5IDEzMC45MzEgMTI0LjUzOUMxMzAuNTM3IDEyNC4wMDkgMTMwLjIzNiAxMjMuMzg2IDEzMC4wMjggMTIyLjY3QzEyOS44MjEgMTIxLjk0NyAxMjkuNzE3IDEyMS4xNjIgMTI5LjcxNyAxMjAuMzE3Wk0xMzIuMzA2IDEyMC4wOTJWMTIwLjMxN0MxMzIuMzA2IDEyMC44NDcgMTMyLjM1MiAxMjEuMzQxIDEzMi40NDUgMTIxLjhDMTMyLjU0NiAxMjIuMjU4IDEzMi43IDEyMi42NjMgMTMyLjkwNyAxMjMuMDE0QzEzMy4xMTUgMTIzLjM1NyAxMzMuMzgzIDEyMy42MyAxMzMuNzEzIDEyMy44M0MxMzQuMDQ5IDEyNC4wMjMgMTM0LjQ1MSAxMjQuMTIgMTM0LjkxNiAxMjQuMTJDMTM1LjUwMyAxMjQuMTIgMTM1Ljk4NyAxMjMuOTkxIDEzNi4zNjYgMTIzLjczM0MxMzYuNzQ2IDEyMy40NzYgMTM3LjA0MyAxMjMuMTI4IDEzNy4yNTggMTIyLjY5MUMxMzcuNDggMTIyLjI0NyAxMzcuNjMgMTIxLjc1MyAxMzcuNzA5IDEyMS4yMDlWMTE5LjI2NUMxMzcuNjY2IDExOC44NDIgMTM3LjU3NiAxMTguNDQ4IDEzNy40NCAxMTguMDgzQzEzNy4zMTIgMTE3LjcxOCAxMzcuMTM2IDExNy4zOTkgMTM2LjkxNCAxMTcuMTI3QzEzNi42OTIgMTE2Ljg0OCAxMzYuNDE2IDExNi42MzMgMTM2LjA4NyAxMTYuNDgyQzEzNS43NjUgMTE2LjMyNSAxMzUuMzgyIDExNi4yNDYgMTM0LjkzOCAxMTYuMjQ2QzEzNC40NjUgMTE2LjI0NiAxMzQuMDY0IDExNi4zNDYgMTMzLjczNCAxMTYuNTQ3QzEzMy40MDUgMTE2Ljc0NyAxMzMuMTMzIDExNy4wMjMgMTMyLjkxOCAxMTcuMzc0QzEzMi43MSAxMTcuNzI1IDEzMi41NTYgMTE4LjEzMyAxMzIuNDU2IDExOC41OTlDMTMyLjM1NiAxMTkuMDY0IDEzMi4zMDYgMTE5LjU2MiAxMzIuMzA2IDEyMC4wOTJaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", "description": null, "order": 7500, "name": "Buttons" 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 4a62cc0598..b14cf633a2 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,7 +2,7 @@ "fqn": "single_switch", "name": "Single Switch", "deprecated": false, - "image": "tb-image:c2luZ2xlLXN3aXRjaC5zdmc=:IlNpbmdsZSBTd2l0Y2giIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAxIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMSAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8zNjM5XzE1NTY3NCkiPgo8cmVjdCB4PSIwLjUiIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMSIgeT0iMC41IiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgcng9IjMuNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHJlY3QgeD0iNTUiIHk9IjcwIiB3aWR0aD0iMzguMDk1MiIgaGVpZ2h0PSIxOS45NTQ3IiByeD0iOS45NzczMyIgZmlsbD0iIzU0NjlGRiIvPgo8Y2lyY2xlIGN4PSI4My4xMTY0IiBjeT0iNzkuOTc3MiIgcj0iOC4xNjMyNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTEwNi42OTQgODEuNzUzNEMxMDYuNjk0IDgxLjUzMzcgMTA2LjY2IDgxLjMzODQgMTA2LjU5MiA4MS4xNjc1QzEwNi41MjggODAuOTk2NiAxMDYuNDE0IDgwLjg0MDMgMTA2LjI0OCA4MC42OTg3QzEwNi4wODIgODAuNTU3MSAxMDUuODQ3IDgwLjQyMDQgMTA1LjU0NCA4MC4yODg2QzEwNS4yNDcgODAuMTUxOSAxMDQuODY2IDgwLjAxMjcgMTA0LjQwMiA3OS44NzExQzEwMy44OTQgNzkuNzE0OCAxMDMuNDI1IDc5LjU0MTUgMTAyLjk5NiA3OS4zNTExQzEwMi41NzEgNzkuMTU1OCAxMDIuMiA3OC45MzEyIDEwMS44ODIgNzguNjc3MkMxMDEuNTY1IDc4LjQxODUgMTAxLjMxOCA3OC4xMjMgMTAxLjE0MyA3Ny43OTFDMTAwLjk2NyA3Ny40NTQxIDEwMC44NzkgNzcuMDY1OSAxMDAuODc5IDc2LjYyNjVDMTAwLjg3OSA3Ni4xOTE5IDEwMC45NjkgNzUuNzk2NCAxMDEuMTUgNzUuNDM5OUMxMDEuMzM1IDc1LjA4MzUgMTAxLjU5NyA3NC43NzU5IDEwMS45MzQgNzQuNTE3MUMxMDIuMjc1IDc0LjI1MzQgMTAyLjY3OCA3NC4wNTA4IDEwMy4xNDIgNzMuOTA5MkMxMDMuNjA2IDczLjc2MjcgMTA0LjExOSA3My42ODk1IDEwNC42OCA3My42ODk1QzEwNS40NzEgNzMuNjg5NSAxMDYuMTUyIDczLjgzNTkgMTA2LjcyNCA3NC4xMjg5QzEwNy4zIDc0LjQyMTkgMTA3Ljc0MiA3NC44MTQ5IDEwOC4wNDkgNzUuMzA4MUMxMDguMzYyIDc1LjgwMTMgMTA4LjUxOCA3Ni4zNDU3IDEwOC41MTggNzYuOTQxNEgxMDYuNjk0QzEwNi42OTQgNzYuNTg5OCAxMDYuNjE5IDc2LjI3OTggMTA2LjQ2NyA3Ni4wMTEyQzEwNi4zMjEgNzUuNzM3OCAxMDYuMDk2IDc1LjUyMjkgMTA1Ljc5MyA3NS4zNjY3QzEwNS40OTYgNzUuMjEwNCAxMDUuMTE3IDc1LjEzMjMgMTA0LjY1OCA3NS4xMzIzQzEwNC4yMjQgNzUuMTMyMyAxMDMuODYyIDc1LjE5ODIgMTAzLjU3NCA3NS4zMzAxQzEwMy4yODYgNzUuNDYxOSAxMDMuMDcxIDc1LjY0MDEgMTAyLjkzIDc1Ljg2NDdDMTAyLjc4OCA3Ni4wODk0IDEwMi43MTcgNzYuMzQzMyAxMDIuNzE3IDc2LjYyNjVDMTAyLjcxNyA3Ni44MjY3IDEwMi43NjQgNzcuMDA5OCAxMDIuODU2IDc3LjE3NThDMTAyLjk0OSA3Ny4zMzY5IDEwMy4wOTEgNzcuNDg4MyAxMDMuMjgxIDc3LjYyOTlDMTAzLjQ3MiA3Ny43NjY2IDEwMy43MTEgNzcuODk2IDEwMy45OTkgNzguMDE4MUMxMDQuMjg3IDc4LjE0MDEgMTA0LjYyNiA3OC4yNTczIDEwNS4wMTcgNzguMzY5NkMxMDUuNjA4IDc4LjU0NTQgMTA2LjEyMyA3OC43NDA3IDEwNi41NjIgNzguOTU1NkMxMDcuMDAyIDc5LjE2NTUgMTA3LjM2OCA3OS40MDQ4IDEwNy42NjEgNzkuNjczM0MxMDcuOTU0IDc5Ljk0MTkgMTA4LjE3NCA4MC4yNDcxIDEwOC4zMiA4MC41ODg5QzEwOC40NjcgODAuOTI1OCAxMDguNTQgODEuMzA5MSAxMDguNTQgODEuNzM4OEMxMDguNTQgODIuMTg4IDEwOC40NSA4Mi41OTMzIDEwOC4yNjkgODIuOTU0NkMxMDguMDg4IDgzLjMxMSAxMDcuODMgODMuNjE2MiAxMDcuNDkzIDgzLjg3MDFDMTA3LjE2MSA4NC4xMTkxIDEwNi43NiA4NC4zMTIgMTA2LjI5MiA4NC40NDg3QzEwNS44MjggODQuNTgwNiAxMDUuMzEgODQuNjQ2NSAxMDQuNzM5IDg0LjY0NjVDMTA0LjIyNiA4NC42NDY1IDEwMy43MjEgODQuNTc4MSAxMDMuMjIzIDg0LjQ0MTRDMTAyLjcyOSA4NC4zMDQ3IDEwMi4yOCA4NC4wOTcyIDEwMS44NzUgODMuODE4OEMxMDEuNDcgODMuNTM1NiAxMDEuMTQ3IDgzLjE4NDEgMTAwLjkwOCA4Mi43NjQyQzEwMC42NjkgODIuMzM5NCAxMDAuNTQ5IDgxLjg0MzggMTAwLjU0OSA4MS4yNzczSDEwMi4zODhDMTAyLjM4OCA4MS42MjQgMTAyLjQ0NiA4MS45MTk0IDEwMi41NjMgODIuMTYzNkMxMDIuNjg2IDgyLjQwNzcgMTAyLjg1NCA4Mi42MDc5IDEwMy4wNjkgODIuNzY0MkMxMDMuMjg0IDgyLjkxNTUgMTAzLjUzMyA4My4wMjc4IDEwMy44MTYgODMuMTAxMUMxMDQuMTA0IDgzLjE3NDMgMTA0LjQxMiA4My4yMTA5IDEwNC43MzkgODMuMjEwOUMxMDUuMTY4IDgzLjIxMDkgMTA1LjUyNyA4My4xNDk5IDEwNS44MTUgODMuMDI3OEMxMDYuMTA4IDgyLjkwNTggMTA2LjMyOCA4Mi43MzQ5IDEwNi40NzUgODIuNTE1MUMxMDYuNjIxIDgyLjI5NTQgMTA2LjY5NCA4Mi4wNDE1IDEwNi42OTQgODEuNzUzNFpNMTEyLjE0NCA4Mi43NDIyTDExMy45NzUgNzYuNTc1MkgxMTUuMTAzTDExNC43OTUgNzguNDIwOUwxMTIuOTQ5IDg0LjVIMTExLjkzOEwxMTIuMTQ0IDgyLjc0MjJaTTExMS4wNjcgNzYuNTc1MkwxMTIuNDk1IDgyLjc3MTVMMTEyLjYxMiA4NC41SDExMS40ODRMMTA5LjMzOCA3Ni41NzUySDExMS4wNjdaTTExNi44MTYgODIuNjk4MkwxMTguMjAxIDc2LjU3NTJIMTE5LjkyMkwxMTcuNzgzIDg0LjVIMTE2LjY1NUwxMTYuODE2IDgyLjY5ODJaTTExNS4yOTMgNzYuNTc1MkwxMTcuMTAyIDgyLjY2ODlMMTE3LjMyOSA4NC41SDExNi4zMThMMTE0LjQ1MSA3OC40MTM2TDExNC4xNDMgNzYuNTc1MkgxMTUuMjkzWk0xMjMuMDEzIDc2LjU3NTJWODQuNUgxMjEuMjRWNzYuNTc1MkgxMjMuMDEzWk0xMjEuMTIzIDc0LjQ5NTFDMTIxLjEyMyA3NC4yMjY2IDEyMS4yMTEgNzQuMDA0NCAxMjEuMzg3IDczLjgyODZDMTIxLjU2NyA3My42NDc5IDEyMS44MTYgNzMuNTU3NiAxMjIuMTM0IDczLjU1NzZDMTIyLjQ0NiA3My41NTc2IDEyMi42OTMgNzMuNjQ3OSAxMjIuODc0IDczLjgyODZDMTIzLjA1NCA3NC4wMDQ0IDEyMy4xNDUgNzQuMjI2NiAxMjMuMTQ1IDc0LjQ5NTFDMTIzLjE0NSA3NC43NTg4IDEyMy4wNTQgNzQuOTc4NSAxMjIuODc0IDc1LjE1NDNDMTIyLjY5MyA3NS4zMzAxIDEyMi40NDYgNzUuNDE4IDEyMi4xMzQgNzUuNDE4QzEyMS44MTYgNzUuNDE4IDEyMS41NjcgNzUuMzMwMSAxMjEuMzg3IDc1LjE1NDNDMTIxLjIxMSA3NC45Nzg1IDEyMS4xMjMgNzQuNzU4OCAxMjEuMTIzIDc0LjQ5NTFaTTEyOC41NzkgNzYuNTc1MlY3Ny44NjQzSDEyNC4xMTFWNzYuNTc1MkgxMjguNTc5Wk0xMjUuNCA3NC42MzQzSDEyNy4xNjZWODIuMzEwMUMxMjcuMTY2IDgyLjU1NDIgMTI3LjIgODIuNzQyMiAxMjcuMjY4IDgyLjg3NEMxMjcuMzQxIDgzLjAwMSAxMjcuNDQxIDgzLjA4NjQgMTI3LjU2OCA4My4xMzA0QzEyNy42OTUgODMuMTc0MyAxMjcuODQ0IDgzLjE5NjMgMTI4LjAxNSA4My4xOTYzQzEyOC4xMzcgODMuMTk2MyAxMjguMjU0IDgzLjE4OSAxMjguMzY3IDgzLjE3NDNDMTI4LjQ3OSA4My4xNTk3IDEyOC41NjkgODMuMTQ1IDEyOC42MzggODMuMTMwNEwxMjguNjQ1IDg0LjQ3OEMxMjguNDk5IDg0LjUyMiAxMjguMzI4IDg0LjU2MSAxMjguMTMyIDg0LjU5NTJDMTI3Ljk0MiA4NC42Mjk0IDEyNy43MjIgODQuNjQ2NSAxMjcuNDczIDg0LjY0NjVDMTI3LjA2OCA4NC42NDY1IDEyNi43MDkgODQuNTc1NyAxMjYuMzk2IDg0LjQzNDFDMTI2LjA4NCA4NC4yODc2IDEyNS44NCA4NC4wNTA4IDEyNS42NjQgODMuNzIzNkMxMjUuNDg4IDgzLjM5NjUgMTI1LjQgODIuOTYxOSAxMjUuNCA4Mi40MTk5Vjc0LjYzNDNaTTEzMy4xNzkgODMuMjQwMkMxMzMuNDY3IDgzLjI0MDIgMTMzLjcyNiA4My4xODQxIDEzMy45NTUgODMuMDcxOEMxMzQuMTg5IDgyLjk1NDYgMTM0LjM3NyA4Mi43OTM1IDEzNC41MTkgODIuNTg4NEMxMzQuNjY2IDgyLjM4MzMgMTM0Ljc0NiA4Mi4xNDY1IDEzNC43NjEgODEuODc3OUgxMzYuNDIzQzEzNi40MTQgODIuMzkwNiAxMzYuMjYyIDgyLjg1NjkgMTM1Ljk2OSA4My4yNzY5QzEzNS42NzYgODMuNjk2OCAxMzUuMjg4IDg0LjAzMTIgMTM0LjgwNSA4NC4yODAzQzEzNC4zMjEgODQuNTI0NCAxMzMuNzg3IDg0LjY0NjUgMTMzLjIwMSA4NC42NDY1QzEzMi41OTUgODQuNjQ2NSAxMzIuMDY4IDg0LjU0MzkgMTMxLjYxOSA4NC4zMzg5QzEzMS4xNjkgODQuMTI4OSAxMzAuNzk2IDgzLjg0MDggMTMwLjQ5OCA4My40NzQ2QzEzMC4yIDgzLjEwODQgMTI5Ljk3NiA4Mi42ODYgMTI5LjgyNCA4Mi4yMDc1QzEyOS42NzggODEuNzI5IDEyOS42MDQgODEuMjE2MyAxMjkuNjA0IDgwLjY2OTRWODAuNDEzMUMxMjkuNjA0IDc5Ljg2NjIgMTI5LjY3OCA3OS4zNTM1IDEyOS44MjQgNzguODc1QzEyOS45NzYgNzguMzkxNiAxMzAuMiA3Ny45NjY4IDEzMC40OTggNzcuNjAwNkMxMzAuNzk2IDc3LjIzNDQgMTMxLjE2OSA3Ni45NDg3IDEzMS42MTkgNzYuNzQzN0MxMzIuMDY4IDc2LjUzMzcgMTMyLjU5MyA3Ni40Mjg3IDEzMy4xOTMgNzYuNDI4N0MxMzMuODI4IDc2LjQyODcgMTM0LjM4NSA3Ni41NTU3IDEzNC44NjMgNzYuODA5NkMxMzUuMzQyIDc3LjA1ODYgMTM1LjcxOCA3Ny40MDc3IDEzNS45OTEgNzcuODU2OUMxMzYuMjcgNzguMzAxMyAxMzYuNDE0IDc4LjgxODggMTM2LjQyMyA3OS40MDk3SDEzNC43NjFDMTM0Ljc0NiA3OS4xMTY3IDEzNC42NzMgNzguODUzIDEzNC41NDEgNzguNjE4N0MxMzQuNDE0IDc4LjM3OTQgMTM0LjIzMyA3OC4xODkgMTMzLjk5OSA3OC4wNDc0QzEzMy43NyA3Ny45MDU4IDEzMy40OTQgNzcuODM1IDEzMy4xNzEgNzcuODM1QzEzMi44MTUgNzcuODM1IDEzMi41MiA3Ny45MDgyIDEzMi4yODUgNzguMDU0N0MxMzIuMDUxIDc4LjE5NjMgMTMxLjg2OCA3OC4zOTE2IDEzMS43MzYgNzguNjQwNkMxMzEuNjA0IDc4Ljg4NDggMTMxLjUwOSA3OS4xNjA2IDEzMS40NSA3OS40NjgzQzEzMS4zOTYgNzkuNzcxIDEzMS4zNyA4MC4wODU5IDEzMS4zNyA4MC40MTMxVjgwLjY2OTRDMTMxLjM3IDgwLjk5NjYgMTMxLjM5NiA4MS4zMTQgMTMxLjQ1IDgxLjYyMTZDMTMxLjUwNCA4MS45MjkyIDEzMS41OTcgODIuMjA1MSAxMzEuNzI5IDgyLjQ0OTJDMTMxLjg2NSA4Mi42ODg1IDEzMi4wNTEgODIuODgxMyAxMzIuMjg1IDgzLjAyNzhDMTMyLjUyIDgzLjE2OTQgMTMyLjgxNyA4My4yNDAyIDEzMy4xNzkgODMuMjQwMlpNMTM5LjUyMSA3My4yNVY4NC41SDEzNy43NjRWNzMuMjVIMTM5LjUyMVpNMTM5LjIxNCA4MC4yNDQ2TDEzOC42NDMgODAuMjM3M0MxMzguNjQ3IDc5LjY5MDQgMTM4LjcyMyA3OS4xODUxIDEzOC44NyA3OC43MjEyQzEzOS4wMjEgNzguMjU3MyAxMzkuMjMxIDc3Ljg1NDUgMTM5LjUgNzcuNTEyN0MxMzkuNzczIDc3LjE2NiAxNDAuMSA3Ni44OTk5IDE0MC40ODEgNzYuNzE0NEMxNDAuODYyIDc2LjUyMzkgMTQxLjI4NCA3Ni40Mjg3IDE0MS43NDggNzYuNDI4N0MxNDIuMTM5IDc2LjQyODcgMTQyLjQ5IDc2LjQ4MjQgMTQyLjgwMyA3Ni41ODk4QzE0My4xMiA3Ni42OTczIDE0My4zOTQgNzYuODcwNiAxNDMuNjIzIDc3LjEwOTlDMTQzLjg1MyA3Ny4zNDQyIDE0NC4wMjYgNzcuNjUxOSAxNDQuMTQzIDc4LjAzMjdDMTQ0LjI2NSA3OC40MDg3IDE0NC4zMjYgNzguODY3NyAxNDQuMzI2IDc5LjQwOTdWODQuNUgxNDIuNTU0Vjc5LjM5NUMxNDIuNTU0IDc5LjAxNDIgMTQyLjQ5OCA3OC43MTE0IDE0Mi4zODUgNzguNDg2OEMxNDIuMjc4IDc4LjI2MjIgMTQyLjExOSA3OC4xMDExIDE0MS45MDkgNzguMDAzNEMxNDEuNjk5IDc3LjkwMDkgMTQxLjQ0MyA3Ny44NDk2IDE0MS4xNCA3Ny44NDk2QzE0MC44MjMgNzcuODQ5NiAxNDAuNTQyIDc3LjkxMzEgMTQwLjI5OCA3OC4wNEMxNDAuMDU5IDc4LjE2NyAxMzkuODU4IDc4LjM0MDMgMTM5LjY5NyA3OC41NjAxQzEzOS41MzYgNzguNzc5OCAxMzkuNDE0IDc5LjAzMzcgMTM5LjMzMSA3OS4zMjE4QzEzOS4yNTMgNzkuNjA5OSAxMzkuMjE0IDc5LjkxNzUgMTM5LjIxNCA4MC4yNDQ2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzM2MzlfMTU1Njc0Ij4KPHJlY3QgeD0iMC41IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", + "image": "tb-image:c2luZ2xlLXN3aXRjaC5zdmc=:IlNpbmdsZSBTd2l0Y2giIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMzEuNTc3MSIgeT0iNjUuMDc0MiIgd2lkdGg9IjU2Ljk5MDEiIGhlaWdodD0iMjkuODUyIiByeD0iMTQuOTI2IiBmaWxsPSIjNTQ2OUZGIi8+CjxjaXJjbGUgY3g9IjczLjY0MDkiIGN5PSI4MC4wMDAzIiByPSIxMi4yMTIyIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTA5LjI0MSA4My40NzE3QzEwOS4yNDEgODMuMTQ5NCAxMDkuMTkxIDgyLjg2MyAxMDkuMDkxIDgyLjYxMjNDMTA4Ljk5OCA4Mi4zNjE3IDEwOC44MjkgODIuMTMyNSAxMDguNTg2IDgxLjkyNDhDMTA4LjM0MiA4MS43MTcxIDEwNy45OTkgODEuNTE2NiAxMDcuNTU1IDgxLjMyMzJDMTA3LjExOCA4MS4xMjI3IDEwNi41NTkgODAuOTE4NiAxMDUuODc5IDgwLjcxMDlDMTA1LjEzNCA4MC40ODE4IDEwNC40NDcgODAuMjI3NSAxMDMuODE2IDc5Ljk0ODJDMTAzLjE5MyA3OS42NjE4IDEwMi42NDkgNzkuMzMyNCAxMDIuMTg0IDc4Ljk2QzEwMS43MTggNzguNTgwNCAxMDEuMzU2IDc4LjE0NzEgMTAxLjA5OSA3Ny42NjAyQzEwMC44NDEgNzcuMTY2IDEwMC43MTIgNzYuNTk2NyAxMDAuNzEyIDc1Ljk1MjFDMTAwLjcxMiA3NS4zMTQ4IDEwMC44NDQgNzQuNzM0NyAxMDEuMTA5IDc0LjIxMTlDMTAxLjM4MiA3My42ODkxIDEwMS43NjUgNzMuMjM4IDEwMi4yNTkgNzIuODU4NEMxMDIuNzYgNzIuNDcxNyAxMDMuMzUxIDcyLjE3NDUgMTA0LjAzMSA3MS45NjY4QzEwNC43MTIgNzEuNzUyIDEwNS40NjQgNzEuNjQ0NSAxMDYuMjg3IDcxLjY0NDVDMTA3LjQ0NyA3MS42NDQ1IDEwOC40NDYgNzEuODU5NCAxMDkuMjg0IDcyLjI4OTFDMTEwLjEyOSA3Mi43MTg4IDExMC43NzcgNzMuMjk1MiAxMTEuMjI5IDc0LjAxODZDMTExLjY4NyA3NC43NDE5IDExMS45MTYgNzUuNTQwNCAxMTEuOTE2IDc2LjQxNDFIMTA5LjI0MUMxMDkuMjQxIDc1Ljg5ODQgMTA5LjEzIDc1LjQ0MzcgMTA4LjkwOCA3NS4wNDk4QzEwOC42OTMgNzQuNjQ4OCAxMDguMzY0IDc0LjMzMzcgMTA3LjkyIDc0LjEwNDVDMTA3LjQ4MyA3My44NzUzIDEwNi45MjggNzMuNzYwNyAxMDYuMjU1IDczLjc2MDdDMTA1LjYxOCA3My43NjA3IDEwNS4wODggNzMuODU3NCAxMDQuNjY1IDc0LjA1MDhDMTA0LjI0MyA3NC4yNDQxIDEwMy45MjcgNzQuNTA1NSAxMDMuNzIgNzQuODM1QzEwMy41MTIgNzUuMTY0NCAxMDMuNDA4IDc1LjUzNjggMTAzLjQwOCA3NS45NTIxQzEwMy40MDggNzYuMjQ1OCAxMDMuNDc2IDc2LjUxNDMgMTAzLjYxMiA3Ni43NTc4QzEwMy43NDggNzYuOTk0MSAxMDMuOTU2IDc3LjIxNjEgMTA0LjIzNSA3Ny40MjM4QzEwNC41MTUgNzcuNjI0MyAxMDQuODY2IDc3LjgxNDEgMTA1LjI4OCA3Ny45OTMyQzEwNS43MTEgNzguMTcyMiAxMDYuMjA4IDc4LjM0NDEgMTA2Ljc4MSA3OC41MDg4QzEwNy42NDggNzguNzY2NiAxMDguNDAzIDc5LjA1MzEgMTA5LjA0OCA3OS4zNjgyQzEwOS42OTIgNzkuNjc2MSAxMTAuMjI5IDgwLjAyNyAxMTAuNjU5IDgwLjQyMDlDMTExLjA4OSA4MC44MTQ4IDExMS40MTEgODEuMjYyNCAxMTEuNjI2IDgxLjc2MzdDMTExLjg0MSA4Mi4yNTc4IDExMS45NDggODIuODIgMTExLjk0OCA4My40NTAyQzExMS45NDggODQuMTA5IDExMS44MTYgODQuNzAzNSAxMTEuNTUxIDg1LjIzMzRDMTExLjI4NiA4NS43NTYyIDExMC45MDYgODYuMjAzOCAxMTAuNDEyIDg2LjU3NjJDMTA5LjkyNSA4Ni45NDE0IDEwOS4zMzggODcuMjI0MyAxMDguNjUgODcuNDI0OEMxMDcuOTcgODcuNjE4MiAxMDcuMjExIDg3LjcxNDggMTA2LjM3MyA4Ny43MTQ4QzEwNS42MjEgODcuNzE0OCAxMDQuODggODcuNjE0NiAxMDQuMTQ5IDg3LjQxNDFDMTAzLjQyNiA4Ny4yMTM1IDEwMi43NjcgODYuOTA5MiAxMDIuMTczIDg2LjUwMUMxMDEuNTc4IDg2LjA4NTYgMTAxLjEwNiA4NS41NyAxMDAuNzU1IDg0Ljk1NDFDMTAwLjQwNCA4NC4zMzExIDEwMC4yMjkgODMuNjA0MiAxMDAuMjI5IDgyLjc3MzRIMTAyLjkyNUMxMDIuOTI1IDgzLjI4MTkgMTAzLjAxMSA4My43MTUyIDEwMy4xODMgODQuMDczMkMxMDMuMzYyIDg0LjQzMTMgMTAzLjYwOSA4NC43MjQ5IDEwMy45MjQgODQuOTU0MUMxMDQuMjM5IDg1LjE3NjEgMTA0LjYwNCA4NS4zNDA4IDEwNS4wMiA4NS40NDgyQzEwNS40NDIgODUuNTU1NyAxMDUuODkzIDg1LjYwOTQgMTA2LjM3MyA4NS42MDk0QzEwNy4wMDMgODUuNjA5NCAxMDcuNTMgODUuNTE5OSAxMDcuOTUyIDg1LjM0MDhDMTA4LjM4MiA4NS4xNjE4IDEwOC43MDQgODQuOTExMSAxMDguOTE5IDg0LjU4ODlDMTA5LjEzNCA4NC4yNjY2IDEwOS4yNDEgODMuODk0MiAxMDkuMjQxIDgzLjQ3MTdaTTExNy41NzcgODQuOTIxOUwxMjAuMjYzIDc1Ljg3N0gxMjEuOTE3TDEyMS40NjYgNzguNTg0TDExOC43NTkgODcuNUgxMTcuMjc2TDExNy41NzcgODQuOTIxOVpNMTE1Ljk5OCA3NS44NzdMMTE4LjA5MyA4NC45NjQ4TDExOC4yNjUgODcuNUgxMTYuNjFMMTEzLjQ2MyA3NS44NzdIMTE1Ljk5OFpNMTI0LjQzMSA4NC44NTc0TDEyNi40NjEgNzUuODc3SDEyOC45ODVMMTI1Ljg0OSA4Ny41SDEyNC4xOTRMMTI0LjQzMSA4NC44NTc0Wk0xMjIuMTk2IDc1Ljg3N0wxMjQuODUgODQuODE0NUwxMjUuMTgzIDg3LjVIMTIzLjdMMTIwLjk2MSA3OC41NzMyTDEyMC41MSA3NS44NzdIMTIyLjE5NlpNMTMzLjg2MiA3NS44NzdWODcuNUgxMzEuMjYzVjc1Ljg3N0gxMzMuODYyWk0xMzEuMDkxIDcyLjgyNjJDMTMxLjA5MSA3Mi40MzIzIDEzMS4yMiA3Mi4xMDY0IDEzMS40NzggNzEuODQ4NkMxMzEuNzQzIDcxLjU4MzcgMTMyLjEwOCA3MS40NTEyIDEzMi41NzMgNzEuNDUxMkMxMzMuMDMyIDcxLjQ1MTIgMTMzLjM5MyA3MS41ODM3IDEzMy42NTggNzEuODQ4NkMxMzMuOTIzIDcyLjEwNjQgMTM0LjA1NiA3Mi40MzIzIDEzNC4wNTYgNzIuODI2MkMxMzQuMDU2IDczLjIxMjkgMTMzLjkyMyA3My41MzUyIDEzMy42NTggNzMuNzkzQzEzMy4zOTMgNzQuMDUwOCAxMzMuMDMyIDc0LjE3OTcgMTMyLjU3MyA3NC4xNzk3QzEzMi4xMDggNzQuMTc5NyAxMzEuNzQzIDc0LjA1MDggMTMxLjQ3OCA3My43OTNDMTMxLjIyIDczLjUzNTIgMTMxLjA5MSA3My4yMTI5IDEzMS4wOTEgNzIuODI2MlpNMTQyLjM3IDc1Ljg3N1Y3Ny43Njc2SDEzNS44MTdWNzUuODc3SDE0Mi4zN1pNMTM3LjcwOCA3My4wMzAzSDE0MC4yOTdWODQuMjg4MUMxNDAuMjk3IDg0LjY0NjIgMTQwLjM0NyA4NC45MjE5IDE0MC40NDcgODUuMTE1MkMxNDAuNTU1IDg1LjMwMTQgMTQwLjcwMSA4NS40MjY4IDE0MC44ODggODUuNDkxMkMxNDEuMDc0IDg1LjU1NTcgMTQxLjI5MiA4NS41ODc5IDE0MS41NDMgODUuNTg3OUMxNDEuNzIyIDg1LjU4NzkgMTQxLjg5NCA4NS41NzcxIDE0Mi4wNTkgODUuNTU1N0MxNDIuMjIzIDg1LjUzNDIgMTQyLjM1NiA4NS41MTI3IDE0Mi40NTYgODUuNDkxMkwxNDIuNDY3IDg3LjQ2NzhDMTQyLjI1MiA4Ny41MzIyIDE0Mi4wMDEgODcuNTg5NSAxNDEuNzE1IDg3LjYzOTZDMTQxLjQzNiA4Ny42ODk4IDE0MS4xMTMgODcuNzE0OCAxNDAuNzQ4IDg3LjcxNDhDMTQwLjE1NCA4Ny43MTQ4IDEzOS42MjcgODcuNjExIDEzOS4xNjkgODcuNDAzM0MxMzguNzExIDg3LjE4ODUgMTM4LjM1MyA4Ni44NDExIDEzOC4wOTUgODYuMzYxM0MxMzcuODM3IDg1Ljg4MTUgMTM3LjcwOCA4NS4yNDQxIDEzNy43MDggODQuNDQ5MlY3My4wMzAzWk0xNDkuNDYgODUuNjUyM0MxNDkuODgyIDg1LjY1MjMgMTUwLjI2MiA4NS41NyAxNTAuNTk5IDg1LjQwNTNDMTUwLjk0MiA4NS4yMzM0IDE1MS4yMTggODQuOTk3MSAxNTEuNDI2IDg0LjY5NjNDMTUxLjY0MSA4NC4zOTU1IDE1MS43NTkgODQuMDQ4MiAxNTEuNzggODMuNjU0M0gxNTQuMjE5QzE1NC4yMDQgODQuNDA2MiAxNTMuOTgyIDg1LjA5MDIgMTUzLjU1MyA4NS43MDYxQzE1My4xMjMgODYuMzIxOSAxNTIuNTU0IDg2LjgxMjUgMTUxLjg0NSA4Ny4xNzc3QzE1MS4xMzYgODcuNTM1OCAxNTAuMzUyIDg3LjcxNDggMTQ5LjQ5MiA4Ny43MTQ4QzE0OC42MDQgODcuNzE0OCAxNDcuODMxIDg3LjU2NDUgMTQ3LjE3MiA4Ny4yNjM3QzE0Ni41MTMgODYuOTU1NyAxNDUuOTY1IDg2LjUzMzIgMTQ1LjUyOCA4NS45OTYxQzE0NS4wOTEgODUuNDU5IDE0NC43NjIgODQuODM5NSAxNDQuNTQgODQuMTM3N0MxNDQuMzI1IDgzLjQzNTkgMTQ0LjIxOCA4Mi42ODM5IDE0NC4yMTggODEuODgxOFY4MS41MDU5QzE0NC4yMTggODAuNzAzOCAxNDQuMzI1IDc5Ljk1MTggMTQ0LjU0IDc5LjI1QzE0NC43NjIgNzguNTQxIDE0NS4wOTEgNzcuOTE4IDE0NS41MjggNzcuMzgwOUMxNDUuOTY1IDc2Ljg0MzggMTQ2LjUxMyA3Ni40MjQ4IDE0Ny4xNzIgNzYuMTI0QzE0Ny44MzEgNzUuODE2MSAxNDguNjAxIDc1LjY2MjEgMTQ5LjQ4MSA3NS42NjIxQzE1MC40MTIgNzUuNjYyMSAxNTEuMjI5IDc1Ljg0ODMgMTUxLjkzMSA3Ni4yMjA3QzE1Mi42MzIgNzYuNTg1OSAxNTMuMTg0IDc3LjA5OCAxNTMuNTg1IDc3Ljc1NjhDMTUzLjk5MyA3OC40MDg1IDE1NC4yMDQgNzkuMTY3NiAxNTQuMjE5IDgwLjAzNDJIMTUxLjc4QzE1MS43NTkgNzkuNjA0NSAxNTEuNjUxIDc5LjIxNzggMTUxLjQ1OCA3OC44NzRDMTUxLjI3MiA3OC41MjMxIDE1MS4wMDcgNzguMjQzOCAxNTAuNjYzIDc4LjAzNjFDMTUwLjMyNiA3Ny44Mjg1IDE0OS45MjIgNzcuNzI0NiAxNDkuNDQ5IDc3LjcyNDZDMTQ4LjkyNiA3Ny43MjQ2IDE0OC40OTMgNzcuODMyIDE0OC4xNDkgNzguMDQ2OUMxNDcuODA2IDc4LjI1NDYgMTQ3LjUzNyA3OC41NDEgMTQ3LjM0NCA3OC45MDYyQzE0Ny4xNSA3OS4yNjQzIDE0Ny4wMTEgNzkuNjY4OSAxNDYuOTI1IDgwLjEyMDFDMTQ2Ljg0NiA4MC41NjQxIDE0Ni44MDcgODEuMDI2IDE0Ni44MDcgODEuNTA1OVY4MS44ODE4QzE0Ni44MDcgODIuMzYxNyAxNDYuODQ2IDgyLjgyNzEgMTQ2LjkyNSA4My4yNzgzQzE0Ny4wMDQgODMuNzI5NSAxNDcuMTQgODQuMTM0MSAxNDcuMzMzIDg0LjQ5MjJDMTQ3LjUzNCA4NC44NDMxIDE0Ny44MDYgODUuMTI2IDE0OC4xNDkgODUuMzQwOEMxNDguNDkzIDg1LjU0ODUgMTQ4LjkzIDg1LjY1MjMgMTQ5LjQ2IDg1LjY1MjNaTTE1OS4xMDYgNzFWODcuNUgxNTYuNTI4VjcxSDE1OS4xMDZaTTE1OC42NTUgODEuMjU4OEwxNTcuODE3IDgxLjI0OEMxNTcuODI1IDgwLjQ0NiAxNTcuOTM2IDc5LjcwNDggMTU4LjE1IDc5LjAyNDRDMTU4LjM3MiA3OC4zNDQxIDE1OC42OCA3Ny43NTMzIDE1OS4wNzQgNzcuMjUyQzE1OS40NzUgNzYuNzQzNSAxNTkuOTU1IDc2LjM1MzIgMTYwLjUxNCA3Ni4wODExQzE2MS4wNzIgNzUuODAxOCAxNjEuNjkyIDc1LjY2MjEgMTYyLjM3MiA3NS42NjIxQzE2Mi45NDUgNzUuNjYyMSAxNjMuNDYxIDc1Ljc0MDkgMTYzLjkxOSA3NS44OTg0QzE2NC4zODQgNzYuMDU2IDE2NC43ODUgNzYuMzEwMiAxNjUuMTIyIDc2LjY2MTFDMTY1LjQ1OSA3Ny4wMDQ5IDE2NS43MTMgNzcuNDU2MSAxNjUuODg1IDc4LjAxNDZDMTY2LjA2NCA3OC41NjYxIDE2Ni4xNTMgNzkuMjM5MyAxNjYuMTUzIDgwLjAzNDJWODcuNUgxNjMuNTU0VjgwLjAxMjdDMTYzLjU1NCA3OS40NTQxIDE2My40NzEgNzkuMDEwMSAxNjMuMzA3IDc4LjY4MDdDMTYzLjE0OSA3OC4zNTEyIDE2Mi45MTYgNzguMTE0OSAxNjIuNjA4IDc3Ljk3MTdDMTYyLjMgNzcuODIxMyAxNjEuOTI0IDc3Ljc0NjEgMTYxLjQ4IDc3Ljc0NjFDMTYxLjAxNSA3Ny43NDYxIDE2MC42MDMgNzcuODM5MiAxNjAuMjQ1IDc4LjAyNTRDMTU5Ljg5NCA3OC4yMTE2IDE1OS42MDEgNzguNDY1OCAxNTkuMzY0IDc4Ljc4ODFDMTU5LjEyOCA3OS4xMTA0IDE1OC45NDkgNzkuNDgyNyAxNTguODI3IDc5LjkwNTNDMTU4LjcxMyA4MC4zMjc4IDE1OC42NTUgODAuNzc5IDE1OC42NTUgODEuMjU4OFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9zdmc+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.", "descriptor": { "type": "rpc", From 147f817445d6d83b94e1de0dffd8e85145c3ff8d Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 5 Feb 2024 18:32:31 +0200 Subject: [PATCH 094/128] UI: Command button widget --- .../json/system/widget_bundles/buttons.json | 3 +- .../system/widget_types/command_button.json | 36 +++++++ .../basic/basic-widget-config.module.ts | 12 ++- ...command-button-basic-config.component.html | 59 ++++++++++++ .../command-button-basic-config.component.ts | 85 +++++++++++++++++ .../single-switch-basic-config.component.html | 2 - .../command-button-widget.component.html | 34 +++++++ .../command-button-widget.component.scss | 28 ++++++ .../button/command-button-widget.component.ts | 94 +++++++++++++++++++ .../button/command-button-widget.models.ts | 71 ++++++++++++++ .../lib/rpc/single-switch-widget.component.ts | 16 ++-- ...mand-button-widget-settings.component.html | 49 ++++++++++ ...ommand-button-widget-settings.component.ts | 68 ++++++++++++++ ...t-value-action-settings-panel.component.ts | 9 +- .../set-value-action-settings.component.ts | 5 - ...ngle-switch-widget-settings.component.html | 2 - .../lib/settings/widget-settings.module.ts | 12 ++- .../widget/widget-components.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 5 + 19 files changed, 564 insertions(+), 33 deletions(-) create mode 100644 application/src/main/data/json/system/widget_types/command_button.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/command-button-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/command-button-widget.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/command-button-widget-settings.component.ts diff --git a/application/src/main/data/json/system/widget_bundles/buttons.json b/application/src/main/data/json/system/widget_bundles/buttons.json index b5f728dc25..a48fba892b 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -8,6 +8,7 @@ "name": "Buttons" }, "widgetTypeFqns": [ - "action_button" + "action_button", + "command_button" ] } \ 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..1ffd2326a8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/command_button.json @@ -0,0 +1,36 @@ +{ + "fqn": "command_button", + "name": "Command button", + "deprecated": false, + "image": "tb-image:Y29tbWFuZC1idXR0b24uc3Zn:IkNvbW1hbmQgYnV0dG9uIiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgNzNWNzUuMzMzM0g3NS41MjEzTDY0LjMzMyA4Ni41MjE3TDY1Ljk3OCA4OC4xNjY3TDc3LjE2NjMgNzYuOTc4M1Y4N0g3OS40OTk3VjczSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDgzLjk3MTdDMTAwLjU2NCA4My42NDk0IDEwMC41MTQgODMuMzYzIDEwMC40MTQgODMuMTEyM0MxMDAuMzIxIDgyLjg2MTcgMTAwLjE1MyA4Mi42MzI1IDk5LjkwOTIgODIuNDI0OEM5OS42NjU3IDgyLjIxNzEgOTkuMzIxOSA4Mi4wMTY2IDk4Ljg3NzkgODEuODIzMkM5OC40NDExIDgxLjYyMjcgOTcuODgyNSA4MS40MTg2IDk3LjIwMjEgODEuMjEwOUM5Ni40NTc0IDgwLjk4MTggOTUuNzY5OSA4MC43Mjc1IDk1LjEzOTYgODAuNDQ4MkM5NC41MTY2IDgwLjE2MTggOTMuOTcyMyA3OS44MzI0IDkzLjUwNjggNzkuNDZDOTMuMDQxMyA3OS4wODA0IDkyLjY3OTcgNzguNjQ3MSA5Mi40MjE5IDc4LjE2MDJDOTIuMTY0MSA3Ny42NjYgOTIuMDM1MiA3Ny4wOTY3IDkyLjAzNTIgNzYuNDUyMUM5Mi4wMzUyIDc1LjgxNDggOTIuMTY3NiA3NS4yMzQ3IDkyLjQzMjYgNzQuNzExOUM5Mi43MDQ4IDc0LjE4OTEgOTMuMDg3OSA3My43MzggOTMuNTgyIDczLjM1ODRDOTQuMDgzMyA3Mi45NzE3IDk0LjY3NDIgNzIuNjc0NSA5NS4zNTQ1IDcyLjQ2NjhDOTYuMDM0OCA3Mi4yNTIgOTYuNzg2OCA3Mi4xNDQ1IDk3LjYxMDQgNzIuMTQ0NUM5OC43NzA1IDcyLjE0NDUgOTkuNzY5NSA3Mi4zNTk0IDEwMC42MDcgNzIuNzg5MUMxMDEuNDUyIDczLjIxODggMTAyLjEwMSA3My43OTUyIDEwMi41NTIgNzQuNTE4NkMxMDMuMDEgNzUuMjQxOSAxMDMuMjM5IDc2LjA0MDQgMTAzLjIzOSA3Ni45MTQxSDEwMC41NjRDMTAwLjU2NCA3Ni4zOTg0IDEwMC40NTMgNzUuOTQzNyAxMDAuMjMxIDc1LjU0OThDMTAwLjAxNyA3NS4xNDg4IDk5LjY4NzIgNzQuODMzNyA5OS4yNDMyIDc0LjYwNDVDOTguODA2MyA3NC4zNzUzIDk4LjI1MTMgNzQuMjYwNyA5Ny41NzgxIDc0LjI2MDdDOTYuOTQwOCA3NC4yNjA3IDk2LjQxMDggNzQuMzU3NCA5NS45ODgzIDc0LjU1MDhDOTUuNTY1OCA3NC43NDQxIDk1LjI1MDcgNzUuMDA1NSA5NS4wNDMgNzUuMzM1Qzk0LjgzNTMgNzUuNjY0NCA5NC43MzE0IDc2LjAzNjggOTQuNzMxNCA3Ni40NTIxQzk0LjczMTQgNzYuNzQ1OCA5NC43OTk1IDc3LjAxNDMgOTQuOTM1NSA3Ny4yNTc4Qzk1LjA3MTYgNzcuNDk0MSA5NS4yNzkzIDc3LjcxNjEgOTUuNTU4NiA3Ny45MjM4Qzk1LjgzNzkgNzguMTI0MyA5Ni4xODg4IDc4LjMxNDEgOTYuNjExMyA3OC40OTMyQzk3LjAzMzkgNzguNjcyMiA5Ny41MzE2IDc4Ljg0NDEgOTguMTA0NSA3OS4wMDg4Qzk4Ljk3MSA3OS4yNjY2IDk5LjcyNjYgNzkuNTUzMSAxMDAuMzcxIDc5Ljg2ODJDMTAxLjAxNiA4MC4xNzYxIDEwMS41NTMgODAuNTI3IDEwMS45ODIgODAuOTIwOUMxMDIuNDEyIDgxLjMxNDggMTAyLjczNCA4MS43NjI0IDEwMi45NDkgODIuMjYzN0MxMDMuMTY0IDgyLjc1NzggMTAzLjI3MSA4My4zMiAxMDMuMjcxIDgzLjk1MDJDMTAzLjI3MSA4NC42MDkgMTAzLjEzOSA4NS4yMDM1IDEwMi44NzQgODUuNzMzNEMxMDIuNjA5IDg2LjI1NjIgMTAyLjIyOSA4Ni43MDM4IDEwMS43MzUgODcuMDc2MkMxMDEuMjQ4IDg3LjQ0MTQgMTAwLjY2MSA4Ny43MjQzIDk5Ljk3MzYgODcuOTI0OEM5OS4yOTMzIDg4LjExODIgOTguNTM0MiA4OC4yMTQ4IDk3LjY5NjMgODguMjE0OEM5Ni45NDQzIDg4LjIxNDggOTYuMjAzMSA4OC4xMTQ2IDk1LjQ3MjcgODcuOTE0MUM5NC43NDkzIDg3LjcxMzUgOTQuMDkwNSA4Ny40MDkyIDkzLjQ5NjEgODcuMDAxQzkyLjkwMTcgODYuNTg1NiA5Mi40MjkgODYuMDcgOTIuMDc4MSA4NS40NTQxQzkxLjcyNzIgODQuODMxMSA5MS41NTE4IDg0LjEwNDIgOTEuNTUxOCA4My4yNzM0SDk0LjI0OEM5NC4yNDggODMuNzgxOSA5NC4zMzQgODQuMjE1MiA5NC41MDU5IDg0LjU3MzJDOTQuNjg0OSA4NC45MzEzIDk0LjkzMiA4NS4yMjQ5IDk1LjI0NzEgODUuNDU0MUM5NS41NjIyIDg1LjY3NjEgOTUuOTI3NCA4NS44NDA4IDk2LjM0MjggODUuOTQ4MkM5Ni43NjUzIDg2LjA1NTcgOTcuMjE2NSA4Ni4xMDk0IDk3LjY5NjMgODYuMTA5NEM5OC4zMjY1IDg2LjEwOTQgOTguODUyOSA4Ni4wMTk5IDk5LjI3NTQgODUuODQwOEM5OS43MDUxIDg1LjY2MTggMTAwLjAyNyA4NS40MTExIDEwMC4yNDIgODUuMDg4OUMxMDAuNDU3IDg0Ljc2NjYgMTAwLjU2NCA4NC4zOTQyIDEwMC41NjQgODMuOTcxN1pNMTEwLjc3MiA4OC4yMTQ4QzEwOS45MTMgODguMjE0OCAxMDkuMTM2IDg4LjA3NTIgMTA4LjQ0MSA4Ny43OTU5QzEwNy43NTQgODcuNTA5NCAxMDcuMTY3IDg3LjExMiAxMDYuNjggODYuNjAzNUMxMDYuMiA4Ni4wOTUxIDEwNS44MzEgODUuNDk3MSAxMDUuNTczIDg0LjgwOTZDMTA1LjMxNSA4NC4xMjIxIDEwNS4xODcgODMuMzgwOSAxMDUuMTg3IDgyLjU4NTlWODIuMTU2MkMxMDUuMTg3IDgxLjI0NjcgMTA1LjMxOSA4MC40MjMyIDEwNS41ODQgNzkuNjg1NUMxMDUuODQ5IDc4Ljk0NzkgMTA2LjIxOCA3OC4zMTc3IDEwNi42OSA3Ny43OTQ5QzEwNy4xNjMgNzcuMjY1IDEwNy43MjIgNzYuODYwNCAxMDguMzY2IDc2LjU4MTFDMTA5LjAxMSA3Ni4zMDE4IDEwOS43MDkgNzYuMTYyMSAxMTAuNDYxIDc2LjE2MjFDMTExLjI5MiA3Ni4xNjIxIDExMi4wMTkgNzYuMzAxOCAxMTIuNjQyIDc2LjU4MTFDMTEzLjI2NSA3Ni44NjA0IDExMy43OCA3Ny4yNTQyIDExNC4xODggNzcuNzYyN0MxMTQuNjA0IDc4LjI2NCAxMTQuOTEyIDc4Ljg2MiAxMTUuMTEyIDc5LjU1NjZDMTE1LjMyIDgwLjI1MTMgMTE1LjQyNCA4MS4wMTc2IDExNS40MjQgODEuODU1NVY4Mi45NjE5SDEwNi40NDNWODEuMTAzNUgxMTIuODY3VjgwLjg5OTRDMTEyLjg1MyA4MC40MzM5IDExMi43NiA3OS45OTcxIDExMi41ODggNzkuNTg4OUMxMTIuNDIzIDc5LjE4MDcgMTEyLjE2OSA3OC44NTEyIDExMS44MjUgNzguNjAwNkMxMTEuNDgxIDc4LjM0OTkgMTExLjAyMyA3OC4yMjQ2IDExMC40NSA3OC4yMjQ2QzExMC4wMjEgNzguMjI0NiAxMDkuNjM3IDc4LjMxNzcgMTA5LjMwMSA3OC41MDM5QzEwOC45NzEgNzguNjgyOSAxMDguNjk2IDc4Ljk0NDMgMTA4LjQ3NCA3OS4yODgxQzEwOC4yNTIgNzkuNjMxOCAxMDguMDggODAuMDQ3MiAxMDcuOTU4IDgwLjUzNDJDMTA3Ljg0MyA4MS4wMTQgMTA3Ljc4NiA4MS41NTQ3IDEwNy43ODYgODIuMTU2MlY4Mi41ODU5QzEwNy43ODYgODMuMDk0NCAxMDcuODU0IDgzLjU2NzEgMTA3Ljk5IDg0LjAwMzlDMTA4LjEzMyA4NC40MzM2IDEwOC4zNDEgODQuODA5NiAxMDguNjEzIDg1LjEzMThDMTA4Ljg4NSA4NS40NTQxIDEwOS4yMTUgODUuNzA4MyAxMDkuNjAyIDg1Ljg5NDVDMTA5Ljk4OCA4Ni4wNzM2IDExMC40MjkgODYuMTYzMSAxMTAuOTIzIDg2LjE2MzFDMTExLjU0NiA4Ni4xNjMxIDExMi4xMDEgODYuMDM3OCAxMTIuNTg4IDg1Ljc4NzFDMTEzLjA3NSA4NS41MzY1IDExMy40OTcgODUuMTgyIDExMy44NTUgODQuNzIzNkwxMTUuMjIgODYuMDQ0OUMxMTQuOTY5IDg2LjQxMDIgMTE0LjY0MyA4Ni43NjExIDExNC4yNDIgODcuMDk3N0MxMTMuODQxIDg3LjQyNzEgMTEzLjM1MSA4Ny42OTU2IDExMi43NzEgODcuOTAzM0MxMTIuMTk4IDg4LjExMSAxMTEuNTMyIDg4LjIxNDggMTEwLjc3MiA4OC4yMTQ4Wk0xMjAuMjYxIDc4Ljg1ODRWODhIMTE3LjY3MlY3Ni4zNzdIMTIwLjExTDEyMC4yNjEgNzguODU4NFpNMTE5Ljc5OSA4MS43NTg4TDExOC45NjEgODEuNzQ4QzExOC45NjggODAuOTI0NSAxMTkuMDgzIDgwLjE2ODkgMTE5LjMwNSA3OS40ODE0QzExOS41MzQgNzguNzkzOSAxMTkuODQ5IDc4LjIwMzEgMTIwLjI1IDc3LjcwOUMxMjAuNjU4IDc3LjIxNDggMTIxLjE0NSA3Ni44MzUzIDEyMS43MTEgNzYuNTcwM0MxMjIuMjc3IDc2LjI5ODIgMTIyLjkwNyA3Ni4xNjIxIDEyMy42MDIgNzYuMTYyMUMxMjQuMTYgNzYuMTYyMSAxMjQuNjY1IDc2LjI0MDkgMTI1LjExNiA3Ni4zOTg0QzEyNS41NzUgNzYuNTQ4OCAxMjUuOTY1IDc2Ljc5NTkgMTI2LjI4NyA3Ny4xMzk2QzEyNi42MTcgNzcuNDgzNCAxMjYuODY3IDc3LjkzMSAxMjcuMDM5IDc4LjQ4MjRDMTI3LjIxMSA3OS4wMjY3IDEyNy4yOTcgNzkuNjk2MyAxMjcuMjk3IDgwLjQ5MTJWODhIMTI0LjY5N1Y4MC40ODA1QzEyNC42OTcgNzkuOTIxOSAxMjQuNjE1IDc5LjQ4MTQgMTI0LjQ1IDc5LjE1OTJDMTI0LjI5MyA3OC44Mjk4IDEyNC4wNiA3OC41OTcgMTIzLjc1MiA3OC40NjA5QzEyMy40NTEgNzguMzE3NyAxMjMuMDc1IDc4LjI0NjEgMTIyLjYyNCA3OC4yNDYxQzEyMi4xOCA3OC4yNDYxIDEyMS43ODMgNzguMzM5MiAxMjEuNDMyIDc4LjUyNTRDMTIxLjA4MSA3OC43MTE2IDEyMC43ODQgNzguOTY1OCAxMjAuNTQgNzkuMjg4MUMxMjAuMzA0IDc5LjYxMDQgMTIwLjEyMSA3OS45ODI3IDExOS45OTIgODAuNDA1M0MxMTkuODYzIDgwLjgyNzggMTE5Ljc5OSA4MS4yNzkgMTE5Ljc5OSA4MS43NTg4Wk0xMzcuMjc5IDg1LjU5MzhWNzEuNUgxMzkuODc5Vjg4SDEzNy41MjZMMTM3LjI3OSA4NS41OTM4Wk0xMjkuNzE3IDgyLjMxNzRWODIuMDkxOEMxMjkuNzE3IDgxLjIxMDkgMTI5LjgyMSA4MC40MDg5IDEzMC4wMjggNzkuNjg1NUMxMzAuMjM2IDc4Ljk1NTEgMTMwLjUzNyA3OC4zMjg1IDEzMC45MzEgNzcuODA1N0MxMzEuMzI1IDc3LjI3NTcgMTMxLjgwNCA3Ni44NzExIDEzMi4zNyA3Ni41OTE4QzEzMi45MzYgNzYuMzA1MyAxMzMuNTczIDc2LjE2MjEgMTM0LjI4MiA3Ni4xNjIxQzEzNC45ODQgNzYuMTYyMSAxMzUuNiA3Ni4yOTgyIDEzNi4xMyA3Ni41NzAzQzEzNi42NiA3Ni44NDI0IDEzNy4xMTEgNzcuMjMyNyAxMzcuNDgzIDc3Ljc0MTJDMTM3Ljg1NiA3OC4yNDI1IDEzOC4xNTMgNzguODQ0MSAxMzguMzc1IDc5LjU0NTlDMTM4LjU5NyA4MC4yNDA2IDEzOC43NTUgODEuMDE0IDEzOC44NDggODEuODY2MlY4Mi41ODU5QzEzOC43NTUgODMuNDE2NyAxMzguNTk3IDg0LjE3NTggMTM4LjM3NSA4NC44NjMzQzEzOC4xNTMgODUuNTUwOCAxMzcuODU2IDg2LjE0NTIgMTM3LjQ4MyA4Ni42NDY1QzEzNy4xMTEgODcuMTQ3OCAxMzYuNjU2IDg3LjUzNDUgMTM2LjExOSA4Ny44MDY2QzEzNS41ODkgODguMDc4OCAxMzQuOTcgODguMjE0OCAxMzQuMjYxIDg4LjIxNDhDMTMzLjU1OSA4OC4yMTQ4IDEzMi45MjUgODguMDY4IDEzMi4zNTkgODcuNzc0NEMxMzEuODAxIDg3LjQ4MDggMTMxLjMyNSA4Ny4wNjkgMTMwLjkzMSA4Ni41MzkxQzEzMC41MzcgODYuMDA5MSAxMzAuMjM2IDg1LjM4NjEgMTMwLjAyOCA4NC42Njk5QzEyOS44MjEgODMuOTQ2NiAxMjkuNzE3IDgzLjE2MjQgMTI5LjcxNyA4Mi4zMTc0Wk0xMzIuMzA2IDgyLjA5MThWODIuMzE3NEMxMzIuMzA2IDgyLjg0NzMgMTMyLjM1MiA4My4zNDE1IDEzMi40NDUgODMuNzk5OEMxMzIuNTQ2IDg0LjI1ODEgMTMyLjcgODQuNjYyOCAxMzIuOTA3IDg1LjAxMzdDMTMzLjExNSA4NS4zNTc0IDEzMy4zODMgODUuNjI5NiAxMzMuNzEzIDg1LjgzMDFDMTM0LjA0OSA4Ni4wMjM0IDEzNC40NTEgODYuMTIwMSAxMzQuOTE2IDg2LjEyMDFDMTM1LjUwMyA4Ni4xMjAxIDEzNS45ODcgODUuOTkxMiAxMzYuMzY2IDg1LjczMzRDMTM2Ljc0NiA4NS40NzU2IDEzNy4wNDMgODUuMTI4MyAxMzcuMjU4IDg0LjY5MTRDMTM3LjQ4IDg0LjI0NzQgMTM3LjYzIDgzLjc1MzMgMTM3LjcwOSA4My4yMDlWODEuMjY0NkMxMzcuNjY2IDgwLjg0MjEgMTM3LjU3NiA4MC40NDgyIDEzNy40NCA4MC4wODNDMTM3LjMxMiA3OS43MTc4IDEzNy4xMzYgNzkuMzk5MSAxMzYuOTE0IDc5LjEyN0MxMzYuNjkyIDc4Ljg0NzcgMTM2LjQxNiA3OC42MzI4IDEzNi4wODcgNzguNDgyNEMxMzUuNzY1IDc4LjMyNDkgMTM1LjM4MiA3OC4yNDYxIDEzNC45MzggNzguMjQ2MUMxMzQuNDY1IDc4LjI0NjEgMTM0LjA2NCA3OC4zNDY0IDEzMy43MzQgNzguNTQ2OUMxMzMuNDA1IDc4Ljc0NzQgMTMzLjEzMyA3OS4wMjMxIDEzMi45MTggNzkuMzc0QzEzMi43MSA3OS43MjQ5IDEzMi41NTYgODAuMTMzMSAxMzIuNDU2IDgwLjU5ODZDMTMyLjM1NiA4MS4wNjQxIDEzMi4zMDYgODEuNTYxOCAxMzIuMzA2IDgyLjA5MThaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", + "description": "Sends the command to the device or updates attribute/time-series when the user clicks the button. Widget settings will enable you to configure behavior how to fetch the initial state and what to trigger when click.", + "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" + ] +} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 7c19993eb1..bb31daf7bb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -97,6 +97,9 @@ import { import { ActionButtonBasicConfigComponent } from '@home/components/widget/config/basic/button/action-button-basic-config.component'; +import { + CommandButtonBasicConfigComponent +} from '@home/components/widget/config/basic/button/command-button-basic-config.component'; @NgModule({ declarations: [ @@ -127,7 +130,8 @@ import { RangeChartBasicConfigComponent, BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, - ActionButtonBasicConfigComponent + ActionButtonBasicConfigComponent, + CommandButtonBasicConfigComponent ], imports: [ CommonModule, @@ -162,7 +166,8 @@ import { RangeChartBasicConfigComponent, BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, - ActionButtonBasicConfigComponent + ActionButtonBasicConfigComponent, + CommandButtonBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -191,5 +196,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type + + +
+
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/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 df495e32df..18fa3b5587 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 @@ -36,7 +36,6 @@
widgets.rpc-state.turn-on
widgets.rpc-state.turn-off
+
+
+ +
+ + +
+
+
+ +
+
+
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/rpc/single-switch-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts index 1103afde39..a767d22408 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 @@ -211,13 +211,15 @@ export class SingleSwitchWidgetComponent extends } public onToggleChange(event: MouseEvent) { - 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) - }); + 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) + }); + } } private onValue(value: boolean): void { 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/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 index 37b8f4d37a..d718168b11 100644 --- 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 @@ -22,14 +22,12 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { merge } from 'rxjs'; import { - getValueActions, SetValueAction, - setValueActions, setValueActionsByWidgetType, + setValueActionsByWidgetType, setValueActionTranslations, SetValueSettings, ValueToDataType } 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'; @@ -50,9 +48,6 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen @Input() setValueSettings: SetValueSettings; - @Input() - valueType: ValueType; - @Input() aliasController: IAliasController; @@ -84,8 +79,6 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen functionScopeVariables = this.widgetService.getWidgetScopeVariables(); - ValueType = ValueType; - setValueSettingsFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts index c29cab1643..fce1e23673 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts @@ -30,7 +30,6 @@ import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; 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, widgetType } from '@shared/models/widget.models'; import { isDefinedAndNotNull } from '@core/utils'; @@ -59,9 +58,6 @@ export class SetValueActionSettingsComponent implements OnInit, ControlValueAcce @Input() panelTitle: string; - @Input() - valueType: ValueType; - @Input() aliasController: IAliasController; @@ -118,7 +114,6 @@ export class SetValueActionSettingsComponent implements OnInit, ControlValueAcce const ctx: any = { setValueSettings: this.modelValue, panelTitle: this.panelTitle, - valueType: this.valueType, aliasController: this.aliasController, targetDevice: this.targetDevice, widgetType: this.widgetType 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 b498116317..9350c2d932 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 @@ -35,7 +35,6 @@
widgets.rpc-state.turn-on
widgets.rpc-state.turn-off
Date: Mon, 5 Feb 2024 19:54:04 +0200 Subject: [PATCH 095/128] UI: add pattern support for labels to button widgets. --- .../action-button-widget.component.html | 1 + .../command-button-widget.component.html | 1 + ...t-button-custom-style-panel.component.html | 1 + ...get-button-custom-style-panel.component.ts | 30 +++++++++++++++++-- .../button/widget-button.component.html | 2 +- .../button/widget-button.component.ts | 19 +++++++++++- 6 files changed, 49 insertions(+), 5 deletions(-) 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 index f667e63356..02e2406859 100644 --- 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 @@ -24,6 +24,7 @@ [borderRadius]="borderRadius" [disabled]="disabled" [activated]="activated" + [ctx]="ctx" (clicked)="onClick($event)">
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 index eaf93c2498..a6f01edb67 100644 --- 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 @@ -23,6 +23,7 @@ [appearance]="appearance" [borderRadius]="borderRadius" [disabled]="disabled" + [ctx]="ctx" (clicked)="onClick($event)">
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 index 7e4a316eef..e5750fd4bb 100644 --- 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 @@ -48,6 +48,7 @@ widgets.button.preview
; + @Input() - popover: TbPopoverComponent; + set popover(popover: TbPopoverComponent) { + this.popoverValue = popover; + popover.tbAnimationDone.subscribe(() => { + this.widgetButtonPreview?.validateSize(); + }); + } + + get popover(): TbPopoverComponent { + return this.popoverValue; + } @Output() customStyleApplied = new EventEmitter(); @@ -125,7 +149,7 @@ export class WidgetButtonCustomStylePanelComponent extends PageComponent impleme if (customStyle?.overrideBackgroundColor) { backgroundColor = customStyle?.backgroundColor; } - let dropShadow = this.appearance.type === WidgetButtonType.basic ? false : true; + let dropShadow = this.appearance.type !== WidgetButtonType.basic; if (customStyle?.overrideDropShadow) { dropShadow = customStyle?.dropShadow; } 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 index 9b239db068..03b5dfb75a 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.component.html +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.html @@ -34,6 +34,6 @@ [style.pointer-events]="disableEvents ? 'none' : ''">
{{ appearance.icon }} - {{ appearance.label }} + {{ label$ | async }}
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 index d8fe88b48e..059f263196 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.ts @@ -25,7 +25,8 @@ import { OnInit, Output, Renderer2, - SimpleChanges, ViewChild, + SimpleChanges, + ViewChild, ViewEncapsulation } from '@angular/core'; import { @@ -36,6 +37,8 @@ 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; @@ -81,9 +84,14 @@ export class WidgetButtonComponent implements OnInit, AfterViewInit, OnDestroy, @coerceBoolean() disableEvents = false; + @Input() + ctx: WidgetContext; + @Output() clicked = new EventEmitter(); + label$: Observable; + iconStyle: ComponentStyle = {}; mousePressed = false; @@ -122,11 +130,20 @@ export class WidgetButtonComponent implements OnInit, AfterViewInit, OnDestroy, 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); From 495e34c33718a32b7294c99afeccd362459f011c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 6 Feb 2024 10:18:54 +0200 Subject: [PATCH 096/128] UI: Improve command button widget loading style. Improve single switch widget disabled state style. --- .../dashboard-page.component.ts | 22 +++++-------- .../widget/lib/action/action-widget.scss | 6 ++++ .../command-button-widget.component.html | 3 +- .../rpc/single-switch-widget.component.html | 14 +++++--- .../rpc/single-switch-widget.component.scss | 6 ---- .../lib/rpc/single-switch-widget.component.ts | 33 ++----------------- 6 files changed, 27 insertions(+), 57 deletions(-) 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/widget/lib/action/action-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss index 276fa725b8..6641c2c32e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss @@ -13,6 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +.mdc-linear-progress.tb-action-widget-progress { + position: absolute; + bottom: 0; + left: 0; + right: 0; +} .tb-action-widget-error-container { position: absolute; bottom: 0; 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 index a6f01edb67..26f910fdd7 100644 --- 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 @@ -22,10 +22,11 @@ +
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 879902a3ef..c1c05cf880 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 @@ -22,17 +22,21 @@
- {{ 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 bb5648a16d..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 @@ -39,12 +39,6 @@ $switchColorDisabled: var(--tb-single-switch-color-disabled, #D5D7E5); position: absolute; inset: 12px; } - .tb-single-switch-progress { - position: absolute; - bottom: 0; - left: 0; - right: 0; - } > div.tb-single-switch-title-panel { position: absolute; top: 12px; 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 a767d22408..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 @@ -97,6 +97,8 @@ export class SingleSwitchWidgetComponent extends offLabel = ''; offLabelStyle: ComponentStyle = {}; + disabledColor = 'rgba(0, 0, 0, 0.38)'; + autoScale = false; private panelResize$: ResizeObserver; @@ -129,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`+ @@ -229,33 +227,6 @@ export class SingleSwitchWidgetComponent extends private onDisabled(value: boolean): void { this.disabled = !!value; - if (this.disabled) { - if (this.showLabel) { - this.labelStyle = {...this.labelStyle, color: 'rgba(0, 0, 0, 0.38)'}; - } - if (this.showIcon) { - this.iconStyle = {...this.iconStyle, color: 'rgba(0, 0, 0, 0.38)'}; - } - if (this.showOnLabel) { - this.onLabelStyle = {...this.onLabelStyle, color: 'rgba(0, 0, 0, 0.38)'}; - } - if (this.showOffLabel) { - this.offLabelStyle = {...this.offLabelStyle, color: 'rgba(0, 0, 0, 0.38)'}; - } - } else { - if (this.showLabel) { - this.labelStyle = {...this.labelStyle, color: this.settings.labelColor}; - } - if (this.showIcon) { - this.iconStyle = {...this.iconStyle, color: this.settings.iconColor}; - } - if (this.showOnLabel) { - this.onLabelStyle = {...this.onLabelStyle, color: this.settings.onLabelColor}; - } - if (this.showOffLabel) { - this.offLabelStyle = {...this.offLabelStyle, color: this.settings.offLabelColor}; - } - } this.cd.markForCheck(); } From c17b2d3b487f66c2ce7b31e9d9a7542e91495300 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 6 Feb 2024 11:23:00 +0200 Subject: [PATCH 097/128] UI: Refactoring --- .../vc/repository-settings.component.html | 2 +- ui-ngx/src/app/modules/home/home.component.ts | 18 ++++++++---------- .../modules/home/pages/home-pages.models.ts | 5 ----- .../app/shared/components/page.component.ts | 3 +-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html index 9b1c627ff2..2fda773889 100644 --- a/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html @@ -28,7 +28,7 @@ -
+
diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index 0b41a90ae3..d6c07004c0 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -149,17 +149,15 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni this.hideLoadingBar = false; this.textSearch.reset('', {emitEvent: false}); this.activeComponent = activeComponent; - let showLoadingBar: boolean; - if (isDefined(this.activeComponent.activatedRoute?.snapshot?.data?.showMainLoadingBar)) { - showLoadingBar = this.activeComponent.activatedRoute.snapshot.data.showMainLoadingBar; - } else if (isDefined(this.activeComponent?.showMainLoadingBar)) { - showLoadingBar = this.activeComponent.showMainLoadingBar; - } - if (activeComponent && activeComponent instanceof RouterTabsComponent) { - this.hideLoadingBar = isDefinedAndNotNull(showLoadingBar) ? !showLoadingBar : true; - } else if (activeComponent && activeComponent instanceof PageComponent && isDefinedAndNotNull(showLoadingBar)) { - this.hideLoadingBar = !showLoadingBar; + + 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/pages/home-pages.models.ts b/ui-ngx/src/app/modules/home/pages/home-pages.models.ts index d2b7e9782c..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,8 +30,3 @@ export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction; loadingSubscription: Subscription; From ff6074807ab3b25a571177c3f3c361b01ce188e5 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 6 Feb 2024 13:48:21 +0200 Subject: [PATCH 098/128] SNMP: request chunk sending delay; refactoring --- .../src/main/resources/thingsboard.yml | 7 +- .../transport/snmp/SnmpTransportContext.java | 2 + .../snmp/service/SnmpTransportService.java | 125 +++++--- .../snmp/session/DeviceSessionContext.java | 6 +- .../transport/snmp/session/ScheduledTask.java | 62 ++++ .../transport/snmp/SnmpDeviceSimulatorV2.java | 19 +- .../server/transport/snmp/SnmpTestV2.java | 42 ++- .../snmp-device-profile-transport-config.json | 43 --- .../snmp-device-transport-config-v3.json | 13 - .../snmp-device-transport-config.json | 6 - .../test/resources/snmp_device_profile.json | 289 ++++++++++++++++++ .../service/DefaultTransportService.java | 12 +- .../src/main/resources/tb-snmp-transport.yml | 7 +- 13 files changed, 492 insertions(+), 141 deletions(-) create mode 100644 common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/ScheduledTask.java delete mode 100644 common/transport/snmp/src/test/resources/snmp-device-profile-transport-config.json delete mode 100644 common/transport/snmp/src/test/resources/snmp-device-transport-config-v3.json delete mode 100644 common/transport/snmp/src/test/resources/snmp-device-transport-config.json create mode 100644 common/transport/snmp/src/test/resources/snmp_device_profile.json diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 936f30af60..6a20c3bcfc 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1173,14 +1173,17 @@ 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}" + scheduler_thread_pool_size: "${SNMP_SCHEDULER_THREAD_POOL_SIZE:4}" stats: # Enable/Disable the collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" 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..26a1ef037e 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; @@ -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) { @@ -341,7 +370,7 @@ public class SnmpTransportService implements TbTransportService, CommandResponde .method(SnmpMethod.TRAP) .build(); - responseProcessingExecutor.execute(() -> { + executor.execute(() -> { processResponse(sessionContext, List.of(pdu), requestContext); }); } @@ -352,7 +381,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 +457,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..504b9bdf6d 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 @@ -803,7 +807,11 @@ public class DefaultTransportService extends TransportActivityManager implements .setMethod(method) .setError(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 error event to core", tenantId, deviceId, e); + } } @Override diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 805ab5a8dd..2e69032cb3 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -128,14 +128,17 @@ 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}" + 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. From 1c59cef7eb23892dbb47ceae9bcd98d2cfdba8b6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 6 Feb 2024 13:59:17 +0200 Subject: [PATCH 099/128] Add scheduler_thread_pool_size param description for SNMP --- application/src/main/resources/thingsboard.yml | 1 + transport/snmp/src/main/resources/tb-snmp-transport.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6a20c3bcfc..535d6189c8 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1183,6 +1183,7 @@ transport: 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}" stats: # Enable/Disable the collection of transport statistics diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index 2e69032cb3..a40374e1f6 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -138,6 +138,7 @@ transport: 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. From c6372e6e4e01c88bd18f60a02cf5c226b28f0325 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Tue, 6 Feb 2024 14:25:05 +0200 Subject: [PATCH 100/128] Added extra mapping for the gateway launch command docker compose file --- .../server/dao/util/DeviceConnectivityUtil.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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"); From e502c5ebbad9e6a331ca4ad40a1d950b93cd4200 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 6 Feb 2024 15:07:38 +0200 Subject: [PATCH 101/128] Revert "Init all actors for isolated rule engines" This reverts commit bf0cc22223b94fb0b73c55eba03a0237ca16eae4. --- .../server/actors/app/AppActor.java | 3 ++- .../server/actors/tenant/TenantActor.java | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index fcd6d7a1f9..98e0bf7c25 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -202,7 +202,8 @@ public class AppActor extends ContextAwareActor { return Optional.ofNullable(ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), () -> DefaultActorService.TENANT_DISPATCHER_NAME, () -> new TenantActor.ActorCreator(systemContext, tenantId), - () -> true)); + () -> systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE) || + systemContext.getPartitionService().isManagedByCurrentService(tenantId))); } private void onToEdgeSessionMsg(EdgeSessionMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index 04c0f1c9e9..01b01a7d4a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java @@ -90,16 +90,18 @@ public class TenantActor extends RuleChainManagerActor { isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); if (isRuleEngine) { - try { - if (getApiUsageState().isReExecEnabled()) { - log.debug("[{}] Going to init rule chains", tenantId); - initRuleChains(); - } else { - log.info("[{}] Skip init of the rule chains due to API limits", tenantId); + if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) { + try { + if (getApiUsageState().isReExecEnabled()) { + log.debug("[{}] Going to init rule chains", tenantId); + initRuleChains(); + } else { + log.info("[{}] Skip init of the rule chains due to API limits", tenantId); + } + } catch (Exception e) { + log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); + cantFindTenant = true; } - } catch (Exception e) { - log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); - cantFindTenant = true; } } log.debug("[{}] Tenant actor started.", tenantId); From dec09fc479890cc3d051fa1f7f24854f0e4a7951 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 6 Feb 2024 15:21:57 +0200 Subject: [PATCH 102/128] extracted duplicated string to variable --- .../CassandraBaseTimeseriesDao.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 8d96dd293b..833a18ad39 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -86,6 +86,18 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD public static final String ASC_ORDER = "ASC"; public static final long SECONDS_IN_DAY = TimeUnit.DAYS.toSeconds(1); protected static final List FIXED_PARTITION = List.of(0L); + private static 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; @@ -566,18 +578,7 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD stmtCreationLock.lock(); try { if (saveWithNullStmt == null) { - saveWithNullStmt = prepare(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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + saveWithNullStmt = prepare(INSERT_WITH_NULL); } } finally { stmtCreationLock.unlock(); @@ -591,18 +592,7 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD stmtCreationLock.lock(); try { if (saveWithNullWithTtlStmt == null) { - saveWithNullWithTtlStmt = prepare(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(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) USING TTL ?"); + saveWithNullWithTtlStmt = prepare(INSERT_WITH_NULL + " USING TTL ?"); } } finally { stmtCreationLock.unlock(); From 23545a64838746a4b322bf098defc3346d46a407 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 6 Feb 2024 15:28:55 +0200 Subject: [PATCH 103/128] CassandraBaseTimeseriesDao refactoring --- .../server/dao/timeseries/CassandraBaseTimeseriesDao.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 833a18ad39..6b2ab7e07b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -86,7 +86,7 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD public static final String ASC_ORDER = "ASC"; public static final long SECONDS_IN_DAY = TimeUnit.DAYS.toSeconds(1); protected static final List FIXED_PARTITION = List.of(0L); - private static String INSERT_WITH_NULL = INSERT_INTO + ModelConstants.TS_KV_CF + + protected static final String INSERT_WITH_NULL = INSERT_INTO + ModelConstants.TS_KV_CF + "(" + ModelConstants.ENTITY_TYPE_COLUMN + "," + ModelConstants.ENTITY_ID_COLUMN + "," + ModelConstants.KEY_COLUMN + From cd46ebe53ae7f83163020d7ac986f4b0baa359fa Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 6 Feb 2024 16:57:52 +0200 Subject: [PATCH 104/128] Fix SNMP traps processing --- .../snmp/service/SnmpTransportService.java | 39 +++++++++++-------- .../service/DefaultTransportService.java | 2 +- 2 files changed, 24 insertions(+), 17 deletions(-) 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 26a1ef037e..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 @@ -37,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; @@ -327,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) { @@ -356,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")); 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 504b9bdf6d..14f7bfffff 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 @@ -805,7 +805,7 @@ public class DefaultTransportService extends TransportActivityManager implements .setEntityIdLSB(deviceId.getId().getLeastSignificantBits()) .setServiceId(serviceInfoProvider.getServiceId()) .setMethod(method) - .setError(ExceptionUtils.getStackTrace(error))) + .setError(ExceptionUtils.getRootCauseMessage(error))) .build(); try { sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); From 78e746b10855b2c1f35ed825f3c43d817adfdb12 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 6 Feb 2024 17:47:40 +0200 Subject: [PATCH 105/128] UI: Update toast notification --- .../core/notification/notification.models.ts | 1 + .../home/models/widget-component.models.ts | 21 +++++----- .../components/snack-bar-component.html | 29 ++++++++++---- .../components/snack-bar-component.scss | 38 +++++++++++++++++++ .../app/shared/components/toast.directive.ts | 2 +- 5 files changed, 73 insertions(+), 18 deletions(-) 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/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index a834120dc6..2abb5384b6 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 @@ -347,35 +347,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, @@ -385,7 +385,8 @@ export class WidgetContext { horizontalPosition, target, panelClass: this.widgetNamespace, - forceDismiss: true + forceDismiss: true, + modern })); } diff --git a/ui-ngx/src/app/shared/components/snack-bar-component.html b/ui-ngx/src/app/shared/components/snack-bar-component.html index cb39b98f48..ca70f6b476 100644 --- a/ui-ngx/src/app/shared/components/snack-bar-component.html +++ b/ui-ngx/src/app/shared/components/snack-bar-component.html @@ -15,15 +15,30 @@ 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/toast.directive.ts b/ui-ngx/src/app/shared/components/toast.directive.ts index 6881460f6a..c096ba2a5a 100644 --- a/ui-ngx/src/app/shared/components/toast.directive.ts +++ b/ui-ngx/src/app/shared/components/toast.directive.ts @@ -280,7 +280,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[] { From f51df3211ddbbf642a15307503070e3f0d173a14 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 7 Feb 2024 13:37:14 +0200 Subject: [PATCH 106/128] fixed saveDeviceWithAccessToken method to delete device if credentials saving failed --- .../server/dao/device/DeviceServiceImpl.java | 1 + .../server/dao/service/DeviceServiceTest.java | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) 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..f06dfac9f9 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,6 +157,7 @@ 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); 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..822f26496b 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 @@ -35,8 +35,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,6 +48,7 @@ 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.tenant.TenantProfileService; @@ -129,6 +128,30 @@ 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 testCountByTenantId() { Assert.assertEquals(0, deviceService.countByTenantId(tenantId)); From 07ff729d61b1ade57a3bc202f22b192de65052d1 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 7 Feb 2024 15:23:31 +0200 Subject: [PATCH 107/128] added Transactional for saveDevice(Device device, boolean doValidate) and saveDevice(Device device) --- .../server/dao/device/DeviceServiceImpl.java | 2 + .../server/dao/service/DeviceServiceTest.java | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+) 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 f06dfac9f9..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 @@ -163,11 +163,13 @@ public class DeviceServiceImpl extends AbstractCachedEntityService idComparator = new IdComparator<>(); private TenantId anotherTenantId; @@ -152,6 +160,43 @@ public class DeviceServiceTest extends AbstractServiceTest { 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)); From 986021fb2f753b649d715549623cd96c1cfe4151 Mon Sep 17 00:00:00 2001 From: Ivan Raznatovskyi Date: Thu, 8 Feb 2024 12:06:33 +0200 Subject: [PATCH 108/128] Fixed RPC connectors table collapsing also fixed JSON field height when this widget looks as a column --- .../widget/lib/gateway/gateway-connectors.component.html | 2 +- .../widget/lib/gateway/gateway-connectors.component.scss | 7 +++++++ .../widget/lib/gateway/gateway-connectors.component.ts | 1 + .../app/shared/components/json-object-edit.component.scss | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) 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..eefd412133 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 @@ -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/shared/components/json-object-edit.component.scss b/ui-ngx/src/app/shared/components/json-object-edit.component.scss index eb66f102a3..63884d518f 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.scss +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.scss @@ -52,6 +52,7 @@ width: 100%; min-width: 200px; height: 100%; + min-height: 300px; &:not(.fill-height) { min-height: 200px; From 0b1956be499fd03f89d10ed177e2c3add4db2a04 Mon Sep 17 00:00:00 2001 From: Dmitriymush Date: Thu, 8 Feb 2024 14:30:10 +0200 Subject: [PATCH 109/128] UI: added image caching and async optimisation for map markers rendering --- ui-ngx/src/app/core/http/image.service.ts | 37 ++++++++++++++++--- .../components/widget/lib/maps/leaflet-map.ts | 23 +++++++++--- .../components/widget/lib/maps/markers.ts | 3 ++ 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts index b0fb772130..45ba1bc4ad 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/modules/home/components/widget/lib/maps/leaflet-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/leaflet-map.ts index 7f611bc16a..d58451d802 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 f5f302cee3..3b0c6efc6c 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); }); } From 021252197f861b3796fc314efc0d73d0428fe433 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 8 Feb 2024 14:36:27 +0200 Subject: [PATCH 110/128] Fix compile error for BaseEdgeEventService --- .../org/thingsboard/server/dao/edge/BaseEdgeEventService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 43c3dc4914..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,6 +19,7 @@ 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; @@ -28,7 +29,6 @@ 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; -import org.thingsboard.server.dao.util.limits.RateLimitService; @Service @Slf4j From 7a28286b0530d4199fa8ee7ba41fc1535d5ffa0b Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 8 Feb 2024 14:59:27 +0200 Subject: [PATCH 111/128] Hints and description improvements --- .../data/json/system/widget_bundles/buttons.json | 2 +- .../json/system/widget_types/action_button.json | 6 ++++-- .../json/system/widget_types/command_button.json | 7 +++++-- .../json/system/widget_types/single_switch.json | 7 +++++-- .../src/assets/locale/locale.constant-en_US.json | 14 +++++++------- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/buttons.json b/application/src/main/data/json/system/widget_bundles/buttons.json index a48fba892b..8e7584d7c5 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -3,7 +3,7 @@ "alias": "buttons", "title": "Buttons", "image": "tb-image:YnV0dG9ucy5zdmc=:IkJ1dHRvbnMiIHN5c3RlbSBidW5kbGUgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjEyLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NjMgNTEuMzMzM1Y0NC4zMzMzSDY2LjgzM1Y1MS4zMzMzSDcyLjY2NjNWNDJINzYuMTY2M0w2NC40OTk3IDMxLjVMNTIuODMzIDQySDU2LjMzM1Y1MS4zMzMzSDYyLjE2NjNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDQyLjkzMTZIODYuMjFMODYuMTg4NSA0MC45NjU4SDg5LjY2ODlDOTAuMjU2MiA0MC45NjU4IDkwLjc1MzkgNDAuODc5OSA5MS4xNjIxIDQwLjcwOEM5MS41Nzc1IDQwLjUyOSA5MS44OTI2IDQwLjI3NDcgOTIuMTA3NCAzOS45NDUzQzkyLjMyMjMgMzkuNjA4NyA5Mi40Mjk3IDM5LjIwNDEgOTIuNDI5NyAzOC43MzE0QzkyLjQyOTcgMzguMjA4NyA5Mi4zMjk0IDM3Ljc4MjYgOTIuMTI4OSAzNy40NTMxQzkxLjkyODQgMzcuMTIzNyA5MS42MjA0IDM2Ljg4MzggOTEuMjA1MSAzNi43MzM0QzkwLjc5NjkgMzYuNTgzIDkwLjI3NDEgMzYuNTA3OCA4OS42MzY3IDM2LjUwNzhIODcuMDI2NFY1MEg4NC4zMzAxVjM0LjM1OTRIODkuNjM2N0M5MC40OTYxIDM0LjM1OTQgOTEuMjYyNCAzNC40NDE3IDkxLjkzNTUgMzQuNjA2NEM5Mi42MTU5IDM0Ljc3MTIgOTMuMTkyNCAzNS4wMjkgOTMuNjY1IDM1LjM3OTlDOTQuMTQ0OSAzNS43MjM2IDk0LjUwNjUgMzYuMTYwNSA5NC43NSAzNi42OTA0Qzk1LjAwMDcgMzcuMjIwNCA5NS4xMjYgMzcuODUwNiA5NS4xMjYgMzguNTgxMUM5NS4xMjYgMzkuMjI1NiA5NC45NzIgMzkuODE2NCA5NC42NjQxIDQwLjM1MzVDOTQuMzU2MSA0MC44ODM1IDkzLjkwMTQgNDEuMzE2NyA5My4yOTk4IDQxLjY1MzNDOTIuNjk4MiA0MS45ODk5IDkxLjk0OTkgNDIuMTkwNCA5MS4wNTQ3IDQyLjI1NDlMOTAuMTk1MyA0Mi45MzE2Wk05MC4wNzcxIDUwSDg1LjM2MTNMODYuNTc1MiA0Ny44NjIzSDkwLjA3NzFDOTAuNjg1OSA0Ny44NjIzIDkxLjE5NDMgNDcuNzYyIDkxLjYwMjUgNDcuNTYxNUM5Mi4wMTA3IDQ3LjM1MzggOTIuMzE1MSA0Ny4wNzEgOTIuNTE1NiA0Ni43MTI5QzkyLjcyMzMgNDYuMzQ3NyA5Mi44MjcxIDQ1LjkyMTUgOTIuODI3MSA0NS40MzQ2QzkyLjgyNzEgNDQuOTI2MSA5Mi43Mzc2IDQ0LjQ4NTcgOTIuNTU4NiA0NC4xMTMzQzkyLjM3OTYgNDMuNzMzNyA5Mi4wOTY3IDQzLjQ0MzcgOTEuNzEgNDMuMjQzMkM5MS4zMjMyIDQzLjAzNTUgOTAuODE4NCA0Mi45MzE2IDkwLjE5NTMgNDIuOTMxNkg4Ny4xNjZMODcuMTg3NSA0MC45NjU4SDkxLjEyOTlMOTEuNzQyMiA0MS43MDdDOTIuNjAxNiA0MS43MzU3IDkzLjMwNyA0MS45MjU1IDkzLjg1ODQgNDIuMjc2NEM5NC40MTcgNDIuNjI3MyA5NC44MzI0IDQzLjA4MiA5NS4xMDQ1IDQzLjY0MDZDOTUuMzc2NiA0NC4xOTkyIDk1LjUxMjcgNDQuODAwOCA5NS41MTI3IDQ1LjQ0NTNDOTUuNTEyNyA0Ni40NDA4IDk1LjI5NDMgNDcuMjc1MSA5NC44NTc0IDQ3Ljk0ODJDOTQuNDI3NyA0OC42MjE0IDkzLjgwODMgNDkuMTMzNSA5Mi45OTkgNDkuNDg0NEM5Mi4xODk4IDQ5LjgyODEgOTEuMjE1OCA1MCA5MC4wNzcxIDUwWk0xMDUuMjE2IDQ3LjI2MDdWMzguMzc3SDEwNy44MTVWNTBIMTA1LjM2NkwxMDUuMjE2IDQ3LjI2MDdaTTEwNS41ODEgNDQuODQzOEwxMDYuNDUxIDQ0LjgyMjNDMTA2LjQ1MSA0NS42MDI5IDEwNi4zNjUgNDYuMzIyNiAxMDYuMTkzIDQ2Ljk4MTRDMTA2LjAyMSA0Ny42MzMxIDEwNS43NTcgNDguMjAyNSAxMDUuMzk4IDQ4LjY4OTVDMTA1LjA0IDQ5LjE2OTMgMTA0LjU4MiA0OS41NDUyIDEwNC4wMjMgNDkuODE3NEMxMDMuNDY1IDUwLjA4MjQgMTAyLjc5NSA1MC4yMTQ4IDEwMi4wMTUgNTAuMjE0OEMxMDEuNDQ5IDUwLjIxNDggMTAwLjkzIDUwLjEzMjUgMTAwLjQ1NyA0OS45Njc4Qzk5Ljk4NDQgNDkuODAzMSA5OS41NzYyIDQ5LjU0ODggOTkuMjMyNCA0OS4yMDUxQzk4Ljg5NTggNDguODYxMyA5OC42MzQ0IDQ4LjQxMzcgOTguNDQ4MiA0Ny44NjIzQzk4LjI2MiA0Ny4zMTA5IDk4LjE2ODkgNDYuNjUyIDk4LjE2ODkgNDUuODg1N1YzOC4zNzdIMTAwLjc1OFY0NS45MDcyQzEwMC43NTggNDYuMzI5OCAxMDAuODA4IDQ2LjY4NDIgMTAwLjkwOCA0Ni45NzA3QzEwMS4wMDggNDcuMjUgMTAxLjE0NSA0Ny40NzU2IDEwMS4zMTYgNDcuNjQ3NUMxMDEuNDg4IDQ3LjgxOTMgMTAxLjY4OSA0Ny45NDExIDEwMS45MTggNDguMDEyN0MxMDIuMTQ3IDQ4LjA4NDMgMTAyLjM5MSA0OC4xMjAxIDEwMi42NDggNDguMTIwMUMxMDMuMzg2IDQ4LjEyMDEgMTAzLjk2NiA0Ny45NzY5IDEwNC4zODkgNDcuNjkwNEMxMDQuODE4IDQ3LjM5NjggMTA1LjEyMyA0Ny4wMDI5IDEwNS4zMDIgNDYuNTA4OEMxMDUuNDg4IDQ2LjAxNDYgMTA1LjU4MSA0NS40NTk2IDEwNS41ODEgNDQuODQzOFpNMTE2LjA0NyAzOC4zNzdWNDAuMjY3NkgxMDkuNDk0VjM4LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDM1LjUzMDNIMTEzLjk3NFY0Ni43ODgxQzExMy45NzQgNDcuMTQ2MiAxMTQuMDI0IDQ3LjQyMTkgMTE0LjEyNCA0Ny42MTUyQzExNC4yMzEgNDcuODAxNCAxMTQuMzc4IDQ3LjkyNjggMTE0LjU2NCA0Ny45OTEyQzExNC43NTEgNDguMDU1NyAxMTQuOTY5IDQ4LjA4NzkgMTE1LjIyIDQ4LjA4NzlDMTE1LjM5OSA0OC4wODc5IDExNS41NzEgNDguMDc3MSAxMTUuNzM1IDQ4LjA1NTdDMTE1LjkgNDguMDM0MiAxMTYuMDMzIDQ4LjAxMjcgMTE2LjEzMyA0Ny45OTEyTDExNi4xNDQgNDkuOTY3OEMxMTUuOTI5IDUwLjAzMjIgMTE1LjY3OCA1MC4wODk1IDExNS4zOTIgNTAuMTM5NkMxMTUuMTEyIDUwLjE4OTggMTE0Ljc5IDUwLjIxNDggMTE0LjQyNSA1MC4yMTQ4QzExMy44MyA1MC4yMTQ4IDExMy4zMDQgNTAuMTExIDExMi44NDYgNDkuOTAzM0MxMTIuMzg3IDQ5LjY4ODUgMTEyLjAyOSA0OS4zNDExIDExMS43NzEgNDguODYxM0MxMTEuNTE0IDQ4LjM4MTUgMTExLjM4NSA0Ny43NDQxIDExMS4zODUgNDYuOTQ5MlYzNS41MzAzWk0xMjMuNjIzIDM4LjM3N1Y0MC4yNjc2SDExNy4wN1YzOC4zNzdIMTIzLjYyM1pNMTE4Ljk2MSAzNS41MzAzSDEyMS41NVY0Ni43ODgxQzEyMS41NSA0Ny4xNDYyIDEyMS42IDQ3LjQyMTkgMTIxLjcgNDcuNjE1MkMxMjEuODA4IDQ3LjgwMTQgMTIxLjk1NCA0Ny45MjY4IDEyMi4xNDEgNDcuOTkxMkMxMjIuMzI3IDQ4LjA1NTcgMTIyLjU0NSA0OC4wODc5IDEyMi43OTYgNDguMDg3OUMxMjIuOTc1IDQ4LjA4NzkgMTIzLjE0NyA0OC4wNzcxIDEyMy4zMTIgNDguMDU1N0MxMjMuNDc2IDQ4LjAzNDIgMTIzLjYwOSA0OC4wMTI3IDEyMy43MDkgNDcuOTkxMkwxMjMuNzIgNDkuOTY3OEMxMjMuNTA1IDUwLjAzMjIgMTIzLjI1NCA1MC4wODk1IDEyMi45NjggNTAuMTM5NkMxMjIuNjg4IDUwLjE4OTggMTIyLjM2NiA1MC4yMTQ4IDEyMi4wMDEgNTAuMjE0OEMxMjEuNDA3IDUwLjIxNDggMTIwLjg4IDUwLjExMSAxMjAuNDIyIDQ5LjkwMzNDMTE5Ljk2NCA0OS42ODg1IDExOS42MDUgNDkuMzQxMSAxMTkuMzQ4IDQ4Ljg2MTNDMTE5LjA5IDQ4LjM4MTUgMTE4Ljk2MSA0Ny43NDQxIDExOC45NjEgNDYuOTQ5MlYzNS41MzAzWk0xMjUuMTE5IDQ0LjMxNzRWNDQuMDcwM0MxMjUuMTE5IDQzLjIzMjQgMTI1LjI0MSA0Mi40NTU0IDEyNS40ODQgNDEuNzM5M0MxMjUuNzI4IDQxLjAxNiAxMjYuMDc5IDQwLjM4OTMgMTI2LjUzNyAzOS44NTk0QzEyNy4wMDMgMzkuMzIyMyAxMjcuNTY4IDM4LjkwNjkgMTI4LjIzNCAzOC42MTMzQzEyOC45MDggMzguMzEyNSAxMjkuNjY3IDM4LjE2MjEgMTMwLjUxMiAzOC4xNjIxQzEzMS4zNjQgMzguMTYyMSAxMzIuMTIzIDM4LjMxMjUgMTMyLjc4OSAzOC42MTMzQzEzMy40NjIgMzguOTA2OSAxMzQuMDMyIDM5LjMyMjMgMTM0LjQ5NyAzOS44NTk0QzEzNC45NjMgNDAuMzg5MyAxMzUuMzE3IDQxLjAxNiAxMzUuNTYxIDQxLjczOTNDMTM1LjgwNCA0Mi40NTU0IDEzNS45MjYgNDMuMjMyNCAxMzUuOTI2IDQ0LjA3MDNWNDQuMzE3NEMxMzUuOTI2IDQ1LjE1NTMgMTM1LjgwNCA0NS45MzIzIDEzNS41NjEgNDYuNjQ4NEMxMzUuMzE3IDQ3LjM2NDYgMTM0Ljk2MyA0Ny45OTEyIDEzNC40OTcgNDguNTI4M0MxMzQuMDMyIDQ5LjA1ODMgMTMzLjQ2NiA0OS40NzM2IDEzMi44IDQ5Ljc3NDRDMTMyLjEzNCA1MC4wNjggMTMxLjM3OCA1MC4yMTQ4IDEzMC41MzMgNTAuMjE0OEMxMjkuNjgxIDUwLjIxNDggMTI4LjkxOCA1MC4wNjggMTI4LjI0NSA0OS43NzQ0QzEyNy41NzkgNDkuNDczNiAxMjcuMDEzIDQ5LjA1ODMgMTI2LjU0OCA0OC41MjgzQzEyNi4wODIgNDcuOTkxMiAxMjUuNzI4IDQ3LjM2NDYgMTI1LjQ4NCA0Ni42NDg0QzEyNS4yNDEgNDUuOTMyMyAxMjUuMTE5IDQ1LjE1NTMgMTI1LjExOSA0NC4zMTc0Wk0xMjcuNzA4IDQ0LjA3MDNWNDQuMzE3NEMxMjcuNzA4IDQ0Ljg0MDIgMTI3Ljc2MiA0NS4zMzQzIDEyNy44NjkgNDUuNzk5OEMxMjcuOTc3IDQ2LjI2NTMgMTI4LjE0NSA0Ni42NzM1IDEyOC4zNzQgNDcuMDI0NEMxMjguNjAzIDQ3LjM3NTMgMTI4Ljg5NyA0Ny42NTEgMTI5LjI1NSA0Ny44NTE2QzEyOS42MTMgNDguMDUyMSAxMzAuMDM5IDQ4LjE1MjMgMTMwLjUzMyA0OC4xNTIzQzEzMS4wMTMgNDguMTUyMyAxMzEuNDI4IDQ4LjA1MjEgMTMxLjc3OSA0Ny44NTE2QzEzMi4xMzcgNDcuNjUxIDEzMi40MzEgNDcuMzc1MyAxMzIuNjYgNDcuMDI0NEMxMzIuODg5IDQ2LjY3MzUgMTMzLjA1OCA0Ni4yNjUzIDEzMy4xNjUgNDUuNzk5OEMxMzMuMjggNDUuMzM0MyAxMzMuMzM3IDQ0Ljg0MDIgMTMzLjMzNyA0NC4zMTc0VjQ0LjA3MDNDMTMzLjMzNyA0My41NTQ3IDEzMy4yOCA0My4wNjc3IDEzMy4xNjUgNDIuNjA5NEMxMzMuMDU4IDQyLjE0MzkgMTMyLjg4NiA0MS43MzIxIDEzMi42NDkgNDEuMzc0QzEzMi40MiA0MS4wMTYgMTMyLjEyNyA0MC43MzY3IDEzMS43NjkgNDAuNTM2MUMxMzEuNDE4IDQwLjMyODUgMTMwLjk5OSA0MC4yMjQ2IDEzMC41MTIgNDAuMjI0NkMxMzAuMDI1IDQwLjIyNDYgMTI5LjYwMiA0MC4zMjg1IDEyOS4yNDQgNDAuNTM2MUMxMjguODkzIDQwLjczNjcgMTI4LjYwMyA0MS4wMTYgMTI4LjM3NCA0MS4zNzRDMTI4LjE0NSA0MS43MzIxIDEyNy45NzcgNDIuMTQzOSAxMjcuODY5IDQyLjYwOTRDMTI3Ljc2MiA0My4wNjc3IDEyNy43MDggNDMuNTU0NyAxMjcuNzA4IDQ0LjA3MDNaTTE0MC45MTMgNDAuODU4NFY1MEgxMzguMzI0VjM4LjM3N0gxNDAuNzYzTDE0MC45MTMgNDAuODU4NFpNMTQwLjQ1MSA0My43NTg4TDEzOS42MTMgNDMuNzQ4QzEzOS42MiA0Mi45MjQ1IDEzOS43MzUgNDIuMTY4OSAxMzkuOTU3IDQxLjQ4MTRDMTQwLjE4NiA0MC43OTM5IDE0MC41MDEgNDAuMjAzMSAxNDAuOTAyIDM5LjcwOUMxNDEuMzExIDM5LjIxNDggMTQxLjc5OCAzOC44MzUzIDE0Mi4zNjMgMzguNTcwM0MxNDIuOTI5IDM4LjI5ODIgMTQzLjU1OSAzOC4xNjIxIDE0NC4yNTQgMzguMTYyMUMxNDQuODEyIDM4LjE2MjEgMTQ1LjMxNyAzOC4yNDA5IDE0NS43NjkgMzguMzk4NEMxNDYuMjI3IDM4LjU0ODggMTQ2LjYxNyAzOC43OTU5IDE0Ni45MzkgMzkuMTM5NkMxNDcuMjY5IDM5LjQ4MzQgMTQ3LjUyIDM5LjkzMSAxNDcuNjkxIDQwLjQ4MjRDMTQ3Ljg2MyA0MS4wMjY3IDE0Ny45NDkgNDEuNjk2MyAxNDcuOTQ5IDQyLjQ5MTJWNTBIMTQ1LjM1VjQyLjQ4MDVDMTQ1LjM1IDQxLjkyMTkgMTQ1LjI2NyA0MS40ODE0IDE0NS4xMDMgNDEuMTU5MkMxNDQuOTQ1IDQwLjgyOTggMTQ0LjcxMiA0MC41OTcgMTQ0LjQwNCA0MC40NjA5QzE0NC4xMDQgNDAuMzE3NyAxNDMuNzI4IDQwLjI0NjEgMTQzLjI3NiA0MC4yNDYxQzE0Mi44MzIgNDAuMjQ2MSAxNDIuNDM1IDQwLjMzOTIgMTQyLjA4NCA0MC41MjU0QzE0MS43MzMgNDAuNzExNiAxNDEuNDM2IDQwLjk2NTggMTQxLjE5MiA0MS4yODgxQzE0MC45NTYgNDEuNjEwNCAxNDAuNzczIDQxLjk4MjcgMTQwLjY0NSA0Mi40MDUzQzE0MC41MTYgNDIuODI3OCAxNDAuNDUxIDQzLjI3OSAxNDAuNDUxIDQzLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9Ijg4Ljc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgMTExVjExMy4zMzNINzUuNTIxM0w2NC4zMzMgMTI0LjUyMkw2NS45NzggMTI2LjE2N0w3Ny4xNjYzIDExNC45NzhWMTI1SDc5LjQ5OTdWMTExSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDEyMS45NzJDMTAwLjU2NCAxMjEuNjQ5IDEwMC41MTQgMTIxLjM2MyAxMDAuNDE0IDEyMS4xMTJDMTAwLjMyMSAxMjAuODYyIDEwMC4xNTMgMTIwLjYzMiA5OS45MDkyIDEyMC40MjVDOTkuNjY1NyAxMjAuMjE3IDk5LjMyMTkgMTIwLjAxNyA5OC44Nzc5IDExOS44MjNDOTguNDQxMSAxMTkuNjIzIDk3Ljg4MjUgMTE5LjQxOSA5Ny4yMDIxIDExOS4yMTFDOTYuNDU3NCAxMTguOTgyIDk1Ljc2OTkgMTE4LjcyOCA5NS4xMzk2IDExOC40NDhDOTQuNTE2NiAxMTguMTYyIDkzLjk3MjMgMTE3LjgzMiA5My41MDY4IDExNy40NkM5My4wNDEzIDExNy4wOCA5Mi42Nzk3IDExNi42NDcgOTIuNDIxOSAxMTYuMTZDOTIuMTY0MSAxMTUuNjY2IDkyLjAzNTIgMTE1LjA5NyA5Mi4wMzUyIDExNC40NTJDOTIuMDM1MiAxMTMuODE1IDkyLjE2NzYgMTEzLjIzNSA5Mi40MzI2IDExMi43MTJDOTIuNzA0OCAxMTIuMTg5IDkzLjA4NzkgMTExLjczOCA5My41ODIgMTExLjM1OEM5NC4wODMzIDExMC45NzIgOTQuNjc0MiAxMTAuNjc0IDk1LjM1NDUgMTEwLjQ2N0M5Ni4wMzQ4IDExMC4yNTIgOTYuNzg2OCAxMTAuMTQ1IDk3LjYxMDQgMTEwLjE0NUM5OC43NzA1IDExMC4xNDUgOTkuNzY5NSAxMTAuMzU5IDEwMC42MDcgMTEwLjc4OUMxMDEuNDUyIDExMS4yMTkgMTAyLjEwMSAxMTEuNzk1IDEwMi41NTIgMTEyLjUxOUMxMDMuMDEgMTEzLjI0MiAxMDMuMjM5IDExNC4wNCAxMDMuMjM5IDExNC45MTRIMTAwLjU2NEMxMDAuNTY0IDExNC4zOTggMTAwLjQ1MyAxMTMuOTQ0IDEwMC4yMzEgMTEzLjU1QzEwMC4wMTcgMTEzLjE0OSA5OS42ODcyIDExMi44MzQgOTkuMjQzMiAxMTIuNjA0Qzk4LjgwNjMgMTEyLjM3NSA5OC4yNTEzIDExMi4yNjEgOTcuNTc4MSAxMTIuMjYxQzk2Ljk0MDggMTEyLjI2MSA5Ni40MTA4IDExMi4zNTcgOTUuOTg4MyAxMTIuNTUxQzk1LjU2NTggMTEyLjc0NCA5NS4yNTA3IDExMy4wMDYgOTUuMDQzIDExMy4zMzVDOTQuODM1MyAxMTMuNjY0IDk0LjczMTQgMTE0LjAzNyA5NC43MzE0IDExNC40NTJDOTQuNzMxNCAxMTQuNzQ2IDk0Ljc5OTUgMTE1LjAxNCA5NC45MzU1IDExNS4yNThDOTUuMDcxNiAxMTUuNDk0IDk1LjI3OTMgMTE1LjcxNiA5NS41NTg2IDExNS45MjRDOTUuODM3OSAxMTYuMTI0IDk2LjE4ODggMTE2LjMxNCA5Ni42MTEzIDExNi40OTNDOTcuMDMzOSAxMTYuNjcyIDk3LjUzMTYgMTE2Ljg0NCA5OC4xMDQ1IDExNy4wMDlDOTguOTcxIDExNy4yNjcgOTkuNzI2NiAxMTcuNTUzIDEwMC4zNzEgMTE3Ljg2OEMxMDEuMDE2IDExOC4xNzYgMTAxLjU1MyAxMTguNTI3IDEwMS45ODIgMTE4LjkyMUMxMDIuNDEyIDExOS4zMTUgMTAyLjczNCAxMTkuNzYyIDEwMi45NDkgMTIwLjI2NEMxMDMuMTY0IDEyMC43NTggMTAzLjI3MSAxMjEuMzIgMTAzLjI3MSAxMjEuOTVDMTAzLjI3MSAxMjIuNjA5IDEwMy4xMzkgMTIzLjIwMyAxMDIuODc0IDEyMy43MzNDMTAyLjYwOSAxMjQuMjU2IDEwMi4yMjkgMTI0LjcwNCAxMDEuNzM1IDEyNS4wNzZDMTAxLjI0OCAxMjUuNDQxIDEwMC42NjEgMTI1LjcyNCA5OS45NzM2IDEyNS45MjVDOTkuMjkzMyAxMjYuMTE4IDk4LjUzNDIgMTI2LjIxNSA5Ny42OTYzIDEyNi4yMTVDOTYuOTQ0MyAxMjYuMjE1IDk2LjIwMzEgMTI2LjExNSA5NS40NzI3IDEyNS45MTRDOTQuNzQ5MyAxMjUuNzE0IDk0LjA5MDUgMTI1LjQwOSA5My40OTYxIDEyNS4wMDFDOTIuOTAxNyAxMjQuNTg2IDkyLjQyOSAxMjQuMDcgOTIuMDc4MSAxMjMuNDU0QzkxLjcyNzIgMTIyLjgzMSA5MS41NTE4IDEyMi4xMDQgOTEuNTUxOCAxMjEuMjczSDk0LjI0OEM5NC4yNDggMTIxLjc4MiA5NC4zMzQgMTIyLjIxNSA5NC41MDU5IDEyMi41NzNDOTQuNjg0OSAxMjIuOTMxIDk0LjkzMiAxMjMuMjI1IDk1LjI0NzEgMTIzLjQ1NEM5NS41NjIyIDEyMy42NzYgOTUuOTI3NCAxMjMuODQxIDk2LjM0MjggMTIzLjk0OEM5Ni43NjUzIDEyNC4wNTYgOTcuMjE2NSAxMjQuMTA5IDk3LjY5NjMgMTI0LjEwOUM5OC4zMjY1IDEyNC4xMDkgOTguODUyOSAxMjQuMDIgOTkuMjc1NCAxMjMuODQxQzk5LjcwNTEgMTIzLjY2MiAxMDAuMDI3IDEyMy40MTEgMTAwLjI0MiAxMjMuMDg5QzEwMC40NTcgMTIyLjc2NyAxMDAuNTY0IDEyMi4zOTQgMTAwLjU2NCAxMjEuOTcyWk0xMTAuNzcyIDEyNi4yMTVDMTA5LjkxMyAxMjYuMjE1IDEwOS4xMzYgMTI2LjA3NSAxMDguNDQxIDEyNS43OTZDMTA3Ljc1NCAxMjUuNTA5IDEwNy4xNjcgMTI1LjExMiAxMDYuNjggMTI0LjYwNEMxMDYuMiAxMjQuMDk1IDEwNS44MzEgMTIzLjQ5NyAxMDUuNTczIDEyMi44MUMxMDUuMzE1IDEyMi4xMjIgMTA1LjE4NyAxMjEuMzgxIDEwNS4xODcgMTIwLjU4NlYxMjAuMTU2QzEwNS4xODcgMTE5LjI0NyAxMDUuMzE5IDExOC40MjMgMTA1LjU4NCAxMTcuNjg2QzEwNS44NDkgMTE2Ljk0OCAxMDYuMjE4IDExNi4zMTggMTA2LjY5IDExNS43OTVDMTA3LjE2MyAxMTUuMjY1IDEwNy43MjIgMTE0Ljg2IDEwOC4zNjYgMTE0LjU4MUMxMDkuMDExIDExNC4zMDIgMTA5LjcwOSAxMTQuMTYyIDExMC40NjEgMTE0LjE2MkMxMTEuMjkyIDExNC4xNjIgMTEyLjAxOSAxMTQuMzAyIDExMi42NDIgMTE0LjU4MUMxMTMuMjY1IDExNC44NiAxMTMuNzggMTE1LjI1NCAxMTQuMTg4IDExNS43NjNDMTE0LjYwNCAxMTYuMjY0IDExNC45MTIgMTE2Ljg2MiAxMTUuMTEyIDExNy41NTdDMTE1LjMyIDExOC4yNTEgMTE1LjQyNCAxMTkuMDE4IDExNS40MjQgMTE5Ljg1NVYxMjAuOTYySDEwNi40NDNWMTE5LjEwNEgxMTIuODY3VjExOC44OTlDMTEyLjg1MyAxMTguNDM0IDExMi43NiAxMTcuOTk3IDExMi41ODggMTE3LjU4OUMxMTIuNDIzIDExNy4xODEgMTEyLjE2OSAxMTYuODUxIDExMS44MjUgMTE2LjYwMUMxMTEuNDgxIDExNi4zNSAxMTEuMDIzIDExNi4yMjUgMTEwLjQ1IDExNi4yMjVDMTEwLjAyMSAxMTYuMjI1IDEwOS42MzcgMTE2LjMxOCAxMDkuMzAxIDExNi41MDRDMTA4Ljk3MSAxMTYuNjgzIDEwOC42OTYgMTE2Ljk0NCAxMDguNDc0IDExNy4yODhDMTA4LjI1MiAxMTcuNjMyIDEwOC4wOCAxMTguMDQ3IDEwNy45NTggMTE4LjUzNEMxMDcuODQzIDExOS4wMTQgMTA3Ljc4NiAxMTkuNTU1IDEwNy43ODYgMTIwLjE1NlYxMjAuNTg2QzEwNy43ODYgMTIxLjA5NCAxMDcuODU0IDEyMS41NjcgMTA3Ljk5IDEyMi4wMDRDMTA4LjEzMyAxMjIuNDM0IDEwOC4zNDEgMTIyLjgxIDEwOC42MTMgMTIzLjEzMkMxMDguODg1IDEyMy40NTQgMTA5LjIxNSAxMjMuNzA4IDEwOS42MDIgMTIzLjg5NUMxMDkuOTg4IDEyNC4wNzQgMTEwLjQyOSAxMjQuMTYzIDExMC45MjMgMTI0LjE2M0MxMTEuNTQ2IDEyNC4xNjMgMTEyLjEwMSAxMjQuMDM4IDExMi41ODggMTIzLjc4N0MxMTMuMDc1IDEyMy41MzYgMTEzLjQ5NyAxMjMuMTgyIDExMy44NTUgMTIyLjcyNEwxMTUuMjIgMTI0LjA0NUMxMTQuOTY5IDEyNC40MSAxMTQuNjQzIDEyNC43NjEgMTE0LjI0MiAxMjUuMDk4QzExMy44NDEgMTI1LjQyNyAxMTMuMzUxIDEyNS42OTYgMTEyLjc3MSAxMjUuOTAzQzExMi4xOTggMTI2LjExMSAxMTEuNTMyIDEyNi4yMTUgMTEwLjc3MiAxMjYuMjE1Wk0xMjAuMjYxIDExNi44NThWMTI2SDExNy42NzJWMTE0LjM3N0gxMjAuMTFMMTIwLjI2MSAxMTYuODU4Wk0xMTkuNzk5IDExOS43NTlMMTE4Ljk2MSAxMTkuNzQ4QzExOC45NjggMTE4LjkyNCAxMTkuMDgzIDExOC4xNjkgMTE5LjMwNSAxMTcuNDgxQzExOS41MzQgMTE2Ljc5NCAxMTkuODQ5IDExNi4yMDMgMTIwLjI1IDExNS43MDlDMTIwLjY1OCAxMTUuMjE1IDEyMS4xNDUgMTE0LjgzNSAxMjEuNzExIDExNC41N0MxMjIuMjc3IDExNC4yOTggMTIyLjkwNyAxMTQuMTYyIDEyMy42MDIgMTE0LjE2MkMxMjQuMTYgMTE0LjE2MiAxMjQuNjY1IDExNC4yNDEgMTI1LjExNiAxMTQuMzk4QzEyNS41NzUgMTE0LjU0OSAxMjUuOTY1IDExNC43OTYgMTI2LjI4NyAxMTUuMTRDMTI2LjYxNyAxMTUuNDgzIDEyNi44NjcgMTE1LjkzMSAxMjcuMDM5IDExNi40ODJDMTI3LjIxMSAxMTcuMDI3IDEyNy4yOTcgMTE3LjY5NiAxMjcuMjk3IDExOC40OTFWMTI2SDEyNC42OTdWMTE4LjQ4QzEyNC42OTcgMTE3LjkyMiAxMjQuNjE1IDExNy40ODEgMTI0LjQ1IDExNy4xNTlDMTI0LjI5MyAxMTYuODMgMTI0LjA2IDExNi41OTcgMTIzLjc1MiAxMTYuNDYxQzEyMy40NTEgMTE2LjMxOCAxMjMuMDc1IDExNi4yNDYgMTIyLjYyNCAxMTYuMjQ2QzEyMi4xOCAxMTYuMjQ2IDEyMS43ODMgMTE2LjMzOSAxMjEuNDMyIDExNi41MjVDMTIxLjA4MSAxMTYuNzEyIDEyMC43ODQgMTE2Ljk2NiAxMjAuNTQgMTE3LjI4OEMxMjAuMzA0IDExNy42MSAxMjAuMTIxIDExNy45ODMgMTE5Ljk5MiAxMTguNDA1QzExOS44NjMgMTE4LjgyOCAxMTkuNzk5IDExOS4yNzkgMTE5Ljc5OSAxMTkuNzU5Wk0xMzcuMjc5IDEyMy41OTRWMTA5LjVIMTM5Ljg3OVYxMjZIMTM3LjUyNkwxMzcuMjc5IDEyMy41OTRaTTEyOS43MTcgMTIwLjMxN1YxMjAuMDkyQzEyOS43MTcgMTE5LjIxMSAxMjkuODIxIDExOC40MDkgMTMwLjAyOCAxMTcuNjg2QzEzMC4yMzYgMTE2Ljk1NSAxMzAuNTM3IDExNi4zMjggMTMwLjkzMSAxMTUuODA2QzEzMS4zMjUgMTE1LjI3NiAxMzEuODA0IDExNC44NzEgMTMyLjM3IDExNC41OTJDMTMyLjkzNiAxMTQuMzA1IDEzMy41NzMgMTE0LjE2MiAxMzQuMjgyIDExNC4xNjJDMTM0Ljk4NCAxMTQuMTYyIDEzNS42IDExNC4yOTggMTM2LjEzIDExNC41N0MxMzYuNjYgMTE0Ljg0MiAxMzcuMTExIDExNS4yMzMgMTM3LjQ4MyAxMTUuNzQxQzEzNy44NTYgMTE2LjI0MyAxMzguMTUzIDExNi44NDQgMTM4LjM3NSAxMTcuNTQ2QzEzOC41OTcgMTE4LjI0MSAxMzguNzU1IDExOS4wMTQgMTM4Ljg0OCAxMTkuODY2VjEyMC41ODZDMTM4Ljc1NSAxMjEuNDE3IDEzOC41OTcgMTIyLjE3NiAxMzguMzc1IDEyMi44NjNDMTM4LjE1MyAxMjMuNTUxIDEzNy44NTYgMTI0LjE0NSAxMzcuNDgzIDEyNC42NDZDMTM3LjExMSAxMjUuMTQ4IDEzNi42NTYgMTI1LjUzNSAxMzYuMTE5IDEyNS44MDdDMTM1LjU4OSAxMjYuMDc5IDEzNC45NyAxMjYuMjE1IDEzNC4yNjEgMTI2LjIxNUMxMzMuNTU5IDEyNi4yMTUgMTMyLjkyNSAxMjYuMDY4IDEzMi4zNTkgMTI1Ljc3NEMxMzEuODAxIDEyNS40ODEgMTMxLjMyNSAxMjUuMDY5IDEzMC45MzEgMTI0LjUzOUMxMzAuNTM3IDEyNC4wMDkgMTMwLjIzNiAxMjMuMzg2IDEzMC4wMjggMTIyLjY3QzEyOS44MjEgMTIxLjk0NyAxMjkuNzE3IDEyMS4xNjIgMTI5LjcxNyAxMjAuMzE3Wk0xMzIuMzA2IDEyMC4wOTJWMTIwLjMxN0MxMzIuMzA2IDEyMC44NDcgMTMyLjM1MiAxMjEuMzQxIDEzMi40NDUgMTIxLjhDMTMyLjU0NiAxMjIuMjU4IDEzMi43IDEyMi42NjMgMTMyLjkwNyAxMjMuMDE0QzEzMy4xMTUgMTIzLjM1NyAxMzMuMzgzIDEyMy42MyAxMzMuNzEzIDEyMy44M0MxMzQuMDQ5IDEyNC4wMjMgMTM0LjQ1MSAxMjQuMTIgMTM0LjkxNiAxMjQuMTJDMTM1LjUwMyAxMjQuMTIgMTM1Ljk4NyAxMjMuOTkxIDEzNi4zNjYgMTIzLjczM0MxMzYuNzQ2IDEyMy40NzYgMTM3LjA0MyAxMjMuMTI4IDEzNy4yNTggMTIyLjY5MUMxMzcuNDggMTIyLjI0NyAxMzcuNjMgMTIxLjc1MyAxMzcuNzA5IDEyMS4yMDlWMTE5LjI2NUMxMzcuNjY2IDExOC44NDIgMTM3LjU3NiAxMTguNDQ4IDEzNy40NCAxMTguMDgzQzEzNy4zMTIgMTE3LjcxOCAxMzcuMTM2IDExNy4zOTkgMTM2LjkxNCAxMTcuMTI3QzEzNi42OTIgMTE2Ljg0OCAxMzYuNDE2IDExNi42MzMgMTM2LjA4NyAxMTYuNDgyQzEzNS43NjUgMTE2LjMyNSAxMzUuMzgyIDExNi4yNDYgMTM0LjkzOCAxMTYuMjQ2QzEzNC40NjUgMTE2LjI0NiAxMzQuMDY0IDExNi4zNDYgMTMzLjczNCAxMTYuNTQ3QzEzMy40MDUgMTE2Ljc0NyAxMzMuMTMzIDExNy4wMjMgMTMyLjkxOCAxMTcuMzc0QzEzMi43MSAxMTcuNzI1IDEzMi41NTYgMTE4LjEzMyAxMzIuNDU2IDExOC41OTlDMTMyLjM1NiAxMTkuMDY0IDEzMi4zMDYgMTE5LjU2MiAxMzIuMzA2IDEyMC4wOTJaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", - "description": null, + "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" }, 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 index e6ffa8084e..a73aa198e7 100644 --- a/application/src/main/data/json/system/widget_types/action_button.json +++ b/application/src/main/data/json/system/widget_types/action_button.json @@ -3,7 +3,7 @@ "name": "Action button", "deprecated": false, "image": "tb-image:YWN0aW9uLWJ1dHRvbi5zdmc=:IkFjdGlvbiBidXR0b24iIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTYyLjE2NzMgODkuMzMzM1Y4Mi4zMzMzSDY2LjgzNFY4OS4zMzMzSDcyLjY2NzNWODBINzYuMTY3M0w2NC41MDA3IDY5LjVMNTIuODM0IDgwSDU2LjMzNFY4OS4zMzMzSDYyLjE2NzNaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik05MC4xOTUzIDgwLjkzMTZIODYuMjFMODYuMTg4NSA3OC45NjU4SDg5LjY2ODlDOTAuMjU2MiA3OC45NjU4IDkwLjc1MzkgNzguODc5OSA5MS4xNjIxIDc4LjcwOEM5MS41Nzc1IDc4LjUyOSA5MS44OTI2IDc4LjI3NDcgOTIuMTA3NCA3Ny45NDUzQzkyLjMyMjMgNzcuNjA4NyA5Mi40Mjk3IDc3LjIwNDEgOTIuNDI5NyA3Ni43MzE0QzkyLjQyOTcgNzYuMjA4NyA5Mi4zMjk0IDc1Ljc4MjYgOTIuMTI4OSA3NS40NTMxQzkxLjkyODQgNzUuMTIzNyA5MS42MjA0IDc0Ljg4MzggOTEuMjA1MSA3NC43MzM0QzkwLjc5NjkgNzQuNTgzIDkwLjI3NDEgNzQuNTA3OCA4OS42MzY3IDc0LjUwNzhIODcuMDI2NFY4OEg4NC4zMzAxVjcyLjM1OTRIODkuNjM2N0M5MC40OTYxIDcyLjM1OTQgOTEuMjYyNCA3Mi40NDE3IDkxLjkzNTUgNzIuNjA2NEM5Mi42MTU5IDcyLjc3MTIgOTMuMTkyNCA3My4wMjkgOTMuNjY1IDczLjM3OTlDOTQuMTQ0OSA3My43MjM2IDk0LjUwNjUgNzQuMTYwNSA5NC43NSA3NC42OTA0Qzk1LjAwMDcgNzUuMjIwNCA5NS4xMjYgNzUuODUwNiA5NS4xMjYgNzYuNTgxMUM5NS4xMjYgNzcuMjI1NiA5NC45NzIgNzcuODE2NCA5NC42NjQxIDc4LjM1MzVDOTQuMzU2MSA3OC44ODM1IDkzLjkwMTQgNzkuMzE2NyA5My4yOTk4IDc5LjY1MzNDOTIuNjk4MiA3OS45ODk5IDkxLjk0OTkgODAuMTkwNCA5MS4wNTQ3IDgwLjI1NDlMOTAuMTk1MyA4MC45MzE2Wk05MC4wNzcxIDg4SDg1LjM2MTNMODYuNTc1MiA4NS44NjIzSDkwLjA3NzFDOTAuNjg1OSA4NS44NjIzIDkxLjE5NDMgODUuNzYyIDkxLjYwMjUgODUuNTYxNUM5Mi4wMTA3IDg1LjM1MzggOTIuMzE1MSA4NS4wNzEgOTIuNTE1NiA4NC43MTI5QzkyLjcyMzMgODQuMzQ3NyA5Mi44MjcxIDgzLjkyMTUgOTIuODI3MSA4My40MzQ2QzkyLjgyNzEgODIuOTI2MSA5Mi43Mzc2IDgyLjQ4NTcgOTIuNTU4NiA4Mi4xMTMzQzkyLjM3OTYgODEuNzMzNyA5Mi4wOTY3IDgxLjQ0MzcgOTEuNzEgODEuMjQzMkM5MS4zMjMyIDgxLjAzNTUgOTAuODE4NCA4MC45MzE2IDkwLjE5NTMgODAuOTMxNkg4Ny4xNjZMODcuMTg3NSA3OC45NjU4SDkxLjEyOTlMOTEuNzQyMiA3OS43MDdDOTIuNjAxNiA3OS43MzU3IDkzLjMwNyA3OS45MjU1IDkzLjg1ODQgODAuMjc2NEM5NC40MTcgODAuNjI3MyA5NC44MzI0IDgxLjA4MiA5NS4xMDQ1IDgxLjY0MDZDOTUuMzc2NiA4Mi4xOTkyIDk1LjUxMjcgODIuODAwOCA5NS41MTI3IDgzLjQ0NTNDOTUuNTEyNyA4NC40NDA4IDk1LjI5NDMgODUuMjc1MSA5NC44NTc0IDg1Ljk0ODJDOTQuNDI3NyA4Ni42MjE0IDkzLjgwODMgODcuMTMzNSA5Mi45OTkgODcuNDg0NEM5Mi4xODk4IDg3LjgyODEgOTEuMjE1OCA4OCA5MC4wNzcxIDg4Wk0xMDUuMjE2IDg1LjI2MDdWNzYuMzc3SDEwNy44MTVWODhIMTA1LjM2NkwxMDUuMjE2IDg1LjI2MDdaTTEwNS41ODEgODIuODQzOEwxMDYuNDUxIDgyLjgyMjNDMTA2LjQ1MSA4My42MDI5IDEwNi4zNjUgODQuMzIyNiAxMDYuMTkzIDg0Ljk4MTRDMTA2LjAyMSA4NS42MzMxIDEwNS43NTcgODYuMjAyNSAxMDUuMzk4IDg2LjY4OTVDMTA1LjA0IDg3LjE2OTMgMTA0LjU4MiA4Ny41NDUyIDEwNC4wMjMgODcuODE3NEMxMDMuNDY1IDg4LjA4MjQgMTAyLjc5NSA4OC4yMTQ4IDEwMi4wMTUgODguMjE0OEMxMDEuNDQ5IDg4LjIxNDggMTAwLjkzIDg4LjEzMjUgMTAwLjQ1NyA4Ny45Njc4Qzk5Ljk4NDQgODcuODAzMSA5OS41NzYyIDg3LjU0ODggOTkuMjMyNCA4Ny4yMDUxQzk4Ljg5NTggODYuODYxMyA5OC42MzQ0IDg2LjQxMzcgOTguNDQ4MiA4NS44NjIzQzk4LjI2MiA4NS4zMTA5IDk4LjE2ODkgODQuNjUyIDk4LjE2ODkgODMuODg1N1Y3Ni4zNzdIMTAwLjc1OFY4My45MDcyQzEwMC43NTggODQuMzI5OCAxMDAuODA4IDg0LjY4NDIgMTAwLjkwOCA4NC45NzA3QzEwMS4wMDggODUuMjUgMTAxLjE0NSA4NS40NzU2IDEwMS4zMTYgODUuNjQ3NUMxMDEuNDg4IDg1LjgxOTMgMTAxLjY4OSA4NS45NDExIDEwMS45MTggODYuMDEyN0MxMDIuMTQ3IDg2LjA4NDMgMTAyLjM5MSA4Ni4xMjAxIDEwMi42NDggODYuMTIwMUMxMDMuMzg2IDg2LjEyMDEgMTAzLjk2NiA4NS45NzY5IDEwNC4zODkgODUuNjkwNEMxMDQuODE4IDg1LjM5NjggMTA1LjEyMyA4NS4wMDI5IDEwNS4zMDIgODQuNTA4OEMxMDUuNDg4IDg0LjAxNDYgMTA1LjU4MSA4My40NTk2IDEwNS41ODEgODIuODQzOFpNMTE2LjA0NyA3Ni4zNzdWNzguMjY3NkgxMDkuNDk0Vjc2LjM3N0gxMTYuMDQ3Wk0xMTEuMzg1IDczLjUzMDNIMTEzLjk3NFY4NC43ODgxQzExMy45NzQgODUuMTQ2MiAxMTQuMDI0IDg1LjQyMTkgMTE0LjEyNCA4NS42MTUyQzExNC4yMzEgODUuODAxNCAxMTQuMzc4IDg1LjkyNjggMTE0LjU2NCA4NS45OTEyQzExNC43NTEgODYuMDU1NyAxMTQuOTY5IDg2LjA4NzkgMTE1LjIyIDg2LjA4NzlDMTE1LjM5OSA4Ni4wODc5IDExNS41NzEgODYuMDc3MSAxMTUuNzM1IDg2LjA1NTdDMTE1LjkgODYuMDM0MiAxMTYuMDMzIDg2LjAxMjcgMTE2LjEzMyA4NS45OTEyTDExNi4xNDQgODcuOTY3OEMxMTUuOTI5IDg4LjAzMjIgMTE1LjY3OCA4OC4wODk1IDExNS4zOTIgODguMTM5NkMxMTUuMTEyIDg4LjE4OTggMTE0Ljc5IDg4LjIxNDggMTE0LjQyNSA4OC4yMTQ4QzExMy44MyA4OC4yMTQ4IDExMy4zMDQgODguMTExIDExMi44NDYgODcuOTAzM0MxMTIuMzg3IDg3LjY4ODUgMTEyLjAyOSA4Ny4zNDExIDExMS43NzEgODYuODYxM0MxMTEuNTE0IDg2LjM4MTUgMTExLjM4NSA4NS43NDQxIDExMS4zODUgODQuOTQ5MlY3My41MzAzWk0xMjMuNjIzIDc2LjM3N1Y3OC4yNjc2SDExNy4wN1Y3Ni4zNzdIMTIzLjYyM1pNMTE4Ljk2MSA3My41MzAzSDEyMS41NVY4NC43ODgxQzEyMS41NSA4NS4xNDYyIDEyMS42IDg1LjQyMTkgMTIxLjcgODUuNjE1MkMxMjEuODA4IDg1LjgwMTQgMTIxLjk1NCA4NS45MjY4IDEyMi4xNDEgODUuOTkxMkMxMjIuMzI3IDg2LjA1NTcgMTIyLjU0NSA4Ni4wODc5IDEyMi43OTYgODYuMDg3OUMxMjIuOTc1IDg2LjA4NzkgMTIzLjE0NyA4Ni4wNzcxIDEyMy4zMTIgODYuMDU1N0MxMjMuNDc2IDg2LjAzNDIgMTIzLjYwOSA4Ni4wMTI3IDEyMy43MDkgODUuOTkxMkwxMjMuNzIgODcuOTY3OEMxMjMuNTA1IDg4LjAzMjIgMTIzLjI1NCA4OC4wODk1IDEyMi45NjggODguMTM5NkMxMjIuNjg4IDg4LjE4OTggMTIyLjM2NiA4OC4yMTQ4IDEyMi4wMDEgODguMjE0OEMxMjEuNDA3IDg4LjIxNDggMTIwLjg4IDg4LjExMSAxMjAuNDIyIDg3LjkwMzNDMTE5Ljk2NCA4Ny42ODg1IDExOS42MDUgODcuMzQxMSAxMTkuMzQ4IDg2Ljg2MTNDMTE5LjA5IDg2LjM4MTUgMTE4Ljk2MSA4NS43NDQxIDExOC45NjEgODQuOTQ5MlY3My41MzAzWk0xMjUuMTE5IDgyLjMxNzRWODIuMDcwM0MxMjUuMTE5IDgxLjIzMjQgMTI1LjI0MSA4MC40NTU0IDEyNS40ODQgNzkuNzM5M0MxMjUuNzI4IDc5LjAxNiAxMjYuMDc5IDc4LjM4OTMgMTI2LjUzNyA3Ny44NTk0QzEyNy4wMDMgNzcuMzIyMyAxMjcuNTY4IDc2LjkwNjkgMTI4LjIzNCA3Ni42MTMzQzEyOC45MDggNzYuMzEyNSAxMjkuNjY3IDc2LjE2MjEgMTMwLjUxMiA3Ni4xNjIxQzEzMS4zNjQgNzYuMTYyMSAxMzIuMTIzIDc2LjMxMjUgMTMyLjc4OSA3Ni42MTMzQzEzMy40NjIgNzYuOTA2OSAxMzQuMDMyIDc3LjMyMjMgMTM0LjQ5NyA3Ny44NTk0QzEzNC45NjMgNzguMzg5MyAxMzUuMzE3IDc5LjAxNiAxMzUuNTYxIDc5LjczOTNDMTM1LjgwNCA4MC40NTU0IDEzNS45MjYgODEuMjMyNCAxMzUuOTI2IDgyLjA3MDNWODIuMzE3NEMxMzUuOTI2IDgzLjE1NTMgMTM1LjgwNCA4My45MzIzIDEzNS41NjEgODQuNjQ4NEMxMzUuMzE3IDg1LjM2NDYgMTM0Ljk2MyA4NS45OTEyIDEzNC40OTcgODYuNTI4M0MxMzQuMDMyIDg3LjA1ODMgMTMzLjQ2NiA4Ny40NzM2IDEzMi44IDg3Ljc3NDRDMTMyLjEzNCA4OC4wNjggMTMxLjM3OCA4OC4yMTQ4IDEzMC41MzMgODguMjE0OEMxMjkuNjgxIDg4LjIxNDggMTI4LjkxOCA4OC4wNjggMTI4LjI0NSA4Ny43NzQ0QzEyNy41NzkgODcuNDczNiAxMjcuMDEzIDg3LjA1ODMgMTI2LjU0OCA4Ni41MjgzQzEyNi4wODIgODUuOTkxMiAxMjUuNzI4IDg1LjM2NDYgMTI1LjQ4NCA4NC42NDg0QzEyNS4yNDEgODMuOTMyMyAxMjUuMTE5IDgzLjE1NTMgMTI1LjExOSA4Mi4zMTc0Wk0xMjcuNzA4IDgyLjA3MDNWODIuMzE3NEMxMjcuNzA4IDgyLjg0MDIgMTI3Ljc2MiA4My4zMzQzIDEyNy44NjkgODMuNzk5OEMxMjcuOTc3IDg0LjI2NTMgMTI4LjE0NSA4NC42NzM1IDEyOC4zNzQgODUuMDI0NEMxMjguNjAzIDg1LjM3NTMgMTI4Ljg5NyA4NS42NTEgMTI5LjI1NSA4NS44NTE2QzEyOS42MTMgODYuMDUyMSAxMzAuMDM5IDg2LjE1MjMgMTMwLjUzMyA4Ni4xNTIzQzEzMS4wMTMgODYuMTUyMyAxMzEuNDI4IDg2LjA1MjEgMTMxLjc3OSA4NS44NTE2QzEzMi4xMzcgODUuNjUxIDEzMi40MzEgODUuMzc1MyAxMzIuNjYgODUuMDI0NEMxMzIuODg5IDg0LjY3MzUgMTMzLjA1OCA4NC4yNjUzIDEzMy4xNjUgODMuNzk5OEMxMzMuMjggODMuMzM0MyAxMzMuMzM3IDgyLjg0MDIgMTMzLjMzNyA4Mi4zMTc0VjgyLjA3MDNDMTMzLjMzNyA4MS41NTQ3IDEzMy4yOCA4MS4wNjc3IDEzMy4xNjUgODAuNjA5NEMxMzMuMDU4IDgwLjE0MzkgMTMyLjg4NiA3OS43MzIxIDEzMi42NDkgNzkuMzc0QzEzMi40MiA3OS4wMTYgMTMyLjEyNyA3OC43MzY3IDEzMS43NjkgNzguNTM2MUMxMzEuNDE4IDc4LjMyODUgMTMwLjk5OSA3OC4yMjQ2IDEzMC41MTIgNzguMjI0NkMxMzAuMDI1IDc4LjIyNDYgMTI5LjYwMiA3OC4zMjg1IDEyOS4yNDQgNzguNTM2MUMxMjguODkzIDc4LjczNjcgMTI4LjYwMyA3OS4wMTYgMTI4LjM3NCA3OS4zNzRDMTI4LjE0NSA3OS43MzIxIDEyNy45NzcgODAuMTQzOSAxMjcuODY5IDgwLjYwOTRDMTI3Ljc2MiA4MS4wNjc3IDEyNy43MDggODEuNTU0NyAxMjcuNzA4IDgyLjA3MDNaTTE0MC45MTMgNzguODU4NFY4OEgxMzguMzI0Vjc2LjM3N0gxNDAuNzYzTDE0MC45MTMgNzguODU4NFpNMTQwLjQ1MSA4MS43NTg4TDEzOS42MTMgODEuNzQ4QzEzOS42MiA4MC45MjQ1IDEzOS43MzUgODAuMTY4OSAxMzkuOTU3IDc5LjQ4MTRDMTQwLjE4NiA3OC43OTM5IDE0MC41MDEgNzguMjAzMSAxNDAuOTAyIDc3LjcwOUMxNDEuMzExIDc3LjIxNDggMTQxLjc5OCA3Ni44MzUzIDE0Mi4zNjMgNzYuNTcwM0MxNDIuOTI5IDc2LjI5ODIgMTQzLjU1OSA3Ni4xNjIxIDE0NC4yNTQgNzYuMTYyMUMxNDQuODEyIDc2LjE2MjEgMTQ1LjMxNyA3Ni4yNDA5IDE0NS43NjkgNzYuMzk4NEMxNDYuMjI3IDc2LjU0ODggMTQ2LjYxNyA3Ni43OTU5IDE0Ni45MzkgNzcuMTM5NkMxNDcuMjY5IDc3LjQ4MzQgMTQ3LjUyIDc3LjkzMSAxNDcuNjkxIDc4LjQ4MjRDMTQ3Ljg2MyA3OS4wMjY3IDE0Ny45NDkgNzkuNjk2MyAxNDcuOTQ5IDgwLjQ5MTJWODhIMTQ1LjM1VjgwLjQ4MDVDMTQ1LjM1IDc5LjkyMTkgMTQ1LjI2NyA3OS40ODE0IDE0NS4xMDMgNzkuMTU5MkMxNDQuOTQ1IDc4LjgyOTggMTQ0LjcxMiA3OC41OTcgMTQ0LjQwNCA3OC40NjA5QzE0NC4xMDQgNzguMzE3NyAxNDMuNzI4IDc4LjI0NjEgMTQzLjI3NiA3OC4yNDYxQzE0Mi44MzIgNzguMjQ2MSAxNDIuNDM1IDc4LjMzOTIgMTQyLjA4NCA3OC41MjU0QzE0MS43MzMgNzguNzExNiAxNDEuNDM2IDc4Ljk2NTggMTQxLjE5MiA3OS4yODgxQzE0MC45NTYgNzkuNjEwNCAxNDAuNzczIDc5Ljk4MjcgMTQwLjY0NSA4MC40MDUzQzE0MC41MTYgODAuODI3OCAxNDAuNDUxIDgxLjI3OSAxNDAuNDUxIDgxLjc1ODhaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", - "description": "Action button.", + "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, @@ -22,6 +22,8 @@ "tags": [ "button", "action", - "navigation" + "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 index 1ffd2326a8..dcfeb108f2 100644 --- a/application/src/main/data/json/system/widget_types/command_button.json +++ b/application/src/main/data/json/system/widget_types/command_button.json @@ -3,7 +3,7 @@ "name": "Command button", "deprecated": false, "image": "tb-image:Y29tbWFuZC1idXR0b24uc3Zn:IkNvbW1hbmQgYnV0dG9uIiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjAuNzUiIHk9IjUwLjc1IiB3aWR0aD0iMTk4LjUiIGhlaWdodD0iNTguNSIgcng9IjMuMjUiIHN0cm9rZT0iIzNGNTJERCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHBhdGggZD0iTTY1LjQ5OTcgNzNWNzUuMzMzM0g3NS41MjEzTDY0LjMzMyA4Ni41MjE3TDY1Ljk3OCA4OC4xNjY3TDc3LjE2NjMgNzYuOTc4M1Y4N0g3OS40OTk3VjczSDY1LjQ5OTdaIiBmaWxsPSIjM0Y1MkREIi8+CjxwYXRoIGQ9Ik0xMDAuNTY0IDgzLjk3MTdDMTAwLjU2NCA4My42NDk0IDEwMC41MTQgODMuMzYzIDEwMC40MTQgODMuMTEyM0MxMDAuMzIxIDgyLjg2MTcgMTAwLjE1MyA4Mi42MzI1IDk5LjkwOTIgODIuNDI0OEM5OS42NjU3IDgyLjIxNzEgOTkuMzIxOSA4Mi4wMTY2IDk4Ljg3NzkgODEuODIzMkM5OC40NDExIDgxLjYyMjcgOTcuODgyNSA4MS40MTg2IDk3LjIwMjEgODEuMjEwOUM5Ni40NTc0IDgwLjk4MTggOTUuNzY5OSA4MC43Mjc1IDk1LjEzOTYgODAuNDQ4MkM5NC41MTY2IDgwLjE2MTggOTMuOTcyMyA3OS44MzI0IDkzLjUwNjggNzkuNDZDOTMuMDQxMyA3OS4wODA0IDkyLjY3OTcgNzguNjQ3MSA5Mi40MjE5IDc4LjE2MDJDOTIuMTY0MSA3Ny42NjYgOTIuMDM1MiA3Ny4wOTY3IDkyLjAzNTIgNzYuNDUyMUM5Mi4wMzUyIDc1LjgxNDggOTIuMTY3NiA3NS4yMzQ3IDkyLjQzMjYgNzQuNzExOUM5Mi43MDQ4IDc0LjE4OTEgOTMuMDg3OSA3My43MzggOTMuNTgyIDczLjM1ODRDOTQuMDgzMyA3Mi45NzE3IDk0LjY3NDIgNzIuNjc0NSA5NS4zNTQ1IDcyLjQ2NjhDOTYuMDM0OCA3Mi4yNTIgOTYuNzg2OCA3Mi4xNDQ1IDk3LjYxMDQgNzIuMTQ0NUM5OC43NzA1IDcyLjE0NDUgOTkuNzY5NSA3Mi4zNTk0IDEwMC42MDcgNzIuNzg5MUMxMDEuNDUyIDczLjIxODggMTAyLjEwMSA3My43OTUyIDEwMi41NTIgNzQuNTE4NkMxMDMuMDEgNzUuMjQxOSAxMDMuMjM5IDc2LjA0MDQgMTAzLjIzOSA3Ni45MTQxSDEwMC41NjRDMTAwLjU2NCA3Ni4zOTg0IDEwMC40NTMgNzUuOTQzNyAxMDAuMjMxIDc1LjU0OThDMTAwLjAxNyA3NS4xNDg4IDk5LjY4NzIgNzQuODMzNyA5OS4yNDMyIDc0LjYwNDVDOTguODA2MyA3NC4zNzUzIDk4LjI1MTMgNzQuMjYwNyA5Ny41NzgxIDc0LjI2MDdDOTYuOTQwOCA3NC4yNjA3IDk2LjQxMDggNzQuMzU3NCA5NS45ODgzIDc0LjU1MDhDOTUuNTY1OCA3NC43NDQxIDk1LjI1MDcgNzUuMDA1NSA5NS4wNDMgNzUuMzM1Qzk0LjgzNTMgNzUuNjY0NCA5NC43MzE0IDc2LjAzNjggOTQuNzMxNCA3Ni40NTIxQzk0LjczMTQgNzYuNzQ1OCA5NC43OTk1IDc3LjAxNDMgOTQuOTM1NSA3Ny4yNTc4Qzk1LjA3MTYgNzcuNDk0MSA5NS4yNzkzIDc3LjcxNjEgOTUuNTU4NiA3Ny45MjM4Qzk1LjgzNzkgNzguMTI0MyA5Ni4xODg4IDc4LjMxNDEgOTYuNjExMyA3OC40OTMyQzk3LjAzMzkgNzguNjcyMiA5Ny41MzE2IDc4Ljg0NDEgOTguMTA0NSA3OS4wMDg4Qzk4Ljk3MSA3OS4yNjY2IDk5LjcyNjYgNzkuNTUzMSAxMDAuMzcxIDc5Ljg2ODJDMTAxLjAxNiA4MC4xNzYxIDEwMS41NTMgODAuNTI3IDEwMS45ODIgODAuOTIwOUMxMDIuNDEyIDgxLjMxNDggMTAyLjczNCA4MS43NjI0IDEwMi45NDkgODIuMjYzN0MxMDMuMTY0IDgyLjc1NzggMTAzLjI3MSA4My4zMiAxMDMuMjcxIDgzLjk1MDJDMTAzLjI3MSA4NC42MDkgMTAzLjEzOSA4NS4yMDM1IDEwMi44NzQgODUuNzMzNEMxMDIuNjA5IDg2LjI1NjIgMTAyLjIyOSA4Ni43MDM4IDEwMS43MzUgODcuMDc2MkMxMDEuMjQ4IDg3LjQ0MTQgMTAwLjY2MSA4Ny43MjQzIDk5Ljk3MzYgODcuOTI0OEM5OS4yOTMzIDg4LjExODIgOTguNTM0MiA4OC4yMTQ4IDk3LjY5NjMgODguMjE0OEM5Ni45NDQzIDg4LjIxNDggOTYuMjAzMSA4OC4xMTQ2IDk1LjQ3MjcgODcuOTE0MUM5NC43NDkzIDg3LjcxMzUgOTQuMDkwNSA4Ny40MDkyIDkzLjQ5NjEgODcuMDAxQzkyLjkwMTcgODYuNTg1NiA5Mi40MjkgODYuMDcgOTIuMDc4MSA4NS40NTQxQzkxLjcyNzIgODQuODMxMSA5MS41NTE4IDg0LjEwNDIgOTEuNTUxOCA4My4yNzM0SDk0LjI0OEM5NC4yNDggODMuNzgxOSA5NC4zMzQgODQuMjE1MiA5NC41MDU5IDg0LjU3MzJDOTQuNjg0OSA4NC45MzEzIDk0LjkzMiA4NS4yMjQ5IDk1LjI0NzEgODUuNDU0MUM5NS41NjIyIDg1LjY3NjEgOTUuOTI3NCA4NS44NDA4IDk2LjM0MjggODUuOTQ4MkM5Ni43NjUzIDg2LjA1NTcgOTcuMjE2NSA4Ni4xMDk0IDk3LjY5NjMgODYuMTA5NEM5OC4zMjY1IDg2LjEwOTQgOTguODUyOSA4Ni4wMTk5IDk5LjI3NTQgODUuODQwOEM5OS43MDUxIDg1LjY2MTggMTAwLjAyNyA4NS40MTExIDEwMC4yNDIgODUuMDg4OUMxMDAuNDU3IDg0Ljc2NjYgMTAwLjU2NCA4NC4zOTQyIDEwMC41NjQgODMuOTcxN1pNMTEwLjc3MiA4OC4yMTQ4QzEwOS45MTMgODguMjE0OCAxMDkuMTM2IDg4LjA3NTIgMTA4LjQ0MSA4Ny43OTU5QzEwNy43NTQgODcuNTA5NCAxMDcuMTY3IDg3LjExMiAxMDYuNjggODYuNjAzNUMxMDYuMiA4Ni4wOTUxIDEwNS44MzEgODUuNDk3MSAxMDUuNTczIDg0LjgwOTZDMTA1LjMxNSA4NC4xMjIxIDEwNS4xODcgODMuMzgwOSAxMDUuMTg3IDgyLjU4NTlWODIuMTU2MkMxMDUuMTg3IDgxLjI0NjcgMTA1LjMxOSA4MC40MjMyIDEwNS41ODQgNzkuNjg1NUMxMDUuODQ5IDc4Ljk0NzkgMTA2LjIxOCA3OC4zMTc3IDEwNi42OSA3Ny43OTQ5QzEwNy4xNjMgNzcuMjY1IDEwNy43MjIgNzYuODYwNCAxMDguMzY2IDc2LjU4MTFDMTA5LjAxMSA3Ni4zMDE4IDEwOS43MDkgNzYuMTYyMSAxMTAuNDYxIDc2LjE2MjFDMTExLjI5MiA3Ni4xNjIxIDExMi4wMTkgNzYuMzAxOCAxMTIuNjQyIDc2LjU4MTFDMTEzLjI2NSA3Ni44NjA0IDExMy43OCA3Ny4yNTQyIDExNC4xODggNzcuNzYyN0MxMTQuNjA0IDc4LjI2NCAxMTQuOTEyIDc4Ljg2MiAxMTUuMTEyIDc5LjU1NjZDMTE1LjMyIDgwLjI1MTMgMTE1LjQyNCA4MS4wMTc2IDExNS40MjQgODEuODU1NVY4Mi45NjE5SDEwNi40NDNWODEuMTAzNUgxMTIuODY3VjgwLjg5OTRDMTEyLjg1MyA4MC40MzM5IDExMi43NiA3OS45OTcxIDExMi41ODggNzkuNTg4OUMxMTIuNDIzIDc5LjE4MDcgMTEyLjE2OSA3OC44NTEyIDExMS44MjUgNzguNjAwNkMxMTEuNDgxIDc4LjM0OTkgMTExLjAyMyA3OC4yMjQ2IDExMC40NSA3OC4yMjQ2QzExMC4wMjEgNzguMjI0NiAxMDkuNjM3IDc4LjMxNzcgMTA5LjMwMSA3OC41MDM5QzEwOC45NzEgNzguNjgyOSAxMDguNjk2IDc4Ljk0NDMgMTA4LjQ3NCA3OS4yODgxQzEwOC4yNTIgNzkuNjMxOCAxMDguMDggODAuMDQ3MiAxMDcuOTU4IDgwLjUzNDJDMTA3Ljg0MyA4MS4wMTQgMTA3Ljc4NiA4MS41NTQ3IDEwNy43ODYgODIuMTU2MlY4Mi41ODU5QzEwNy43ODYgODMuMDk0NCAxMDcuODU0IDgzLjU2NzEgMTA3Ljk5IDg0LjAwMzlDMTA4LjEzMyA4NC40MzM2IDEwOC4zNDEgODQuODA5NiAxMDguNjEzIDg1LjEzMThDMTA4Ljg4NSA4NS40NTQxIDEwOS4yMTUgODUuNzA4MyAxMDkuNjAyIDg1Ljg5NDVDMTA5Ljk4OCA4Ni4wNzM2IDExMC40MjkgODYuMTYzMSAxMTAuOTIzIDg2LjE2MzFDMTExLjU0NiA4Ni4xNjMxIDExMi4xMDEgODYuMDM3OCAxMTIuNTg4IDg1Ljc4NzFDMTEzLjA3NSA4NS41MzY1IDExMy40OTcgODUuMTgyIDExMy44NTUgODQuNzIzNkwxMTUuMjIgODYuMDQ0OUMxMTQuOTY5IDg2LjQxMDIgMTE0LjY0MyA4Ni43NjExIDExNC4yNDIgODcuMDk3N0MxMTMuODQxIDg3LjQyNzEgMTEzLjM1MSA4Ny42OTU2IDExMi43NzEgODcuOTAzM0MxMTIuMTk4IDg4LjExMSAxMTEuNTMyIDg4LjIxNDggMTEwLjc3MiA4OC4yMTQ4Wk0xMjAuMjYxIDc4Ljg1ODRWODhIMTE3LjY3MlY3Ni4zNzdIMTIwLjExTDEyMC4yNjEgNzguODU4NFpNMTE5Ljc5OSA4MS43NTg4TDExOC45NjEgODEuNzQ4QzExOC45NjggODAuOTI0NSAxMTkuMDgzIDgwLjE2ODkgMTE5LjMwNSA3OS40ODE0QzExOS41MzQgNzguNzkzOSAxMTkuODQ5IDc4LjIwMzEgMTIwLjI1IDc3LjcwOUMxMjAuNjU4IDc3LjIxNDggMTIxLjE0NSA3Ni44MzUzIDEyMS43MTEgNzYuNTcwM0MxMjIuMjc3IDc2LjI5ODIgMTIyLjkwNyA3Ni4xNjIxIDEyMy42MDIgNzYuMTYyMUMxMjQuMTYgNzYuMTYyMSAxMjQuNjY1IDc2LjI0MDkgMTI1LjExNiA3Ni4zOTg0QzEyNS41NzUgNzYuNTQ4OCAxMjUuOTY1IDc2Ljc5NTkgMTI2LjI4NyA3Ny4xMzk2QzEyNi42MTcgNzcuNDgzNCAxMjYuODY3IDc3LjkzMSAxMjcuMDM5IDc4LjQ4MjRDMTI3LjIxMSA3OS4wMjY3IDEyNy4yOTcgNzkuNjk2MyAxMjcuMjk3IDgwLjQ5MTJWODhIMTI0LjY5N1Y4MC40ODA1QzEyNC42OTcgNzkuOTIxOSAxMjQuNjE1IDc5LjQ4MTQgMTI0LjQ1IDc5LjE1OTJDMTI0LjI5MyA3OC44Mjk4IDEyNC4wNiA3OC41OTcgMTIzLjc1MiA3OC40NjA5QzEyMy40NTEgNzguMzE3NyAxMjMuMDc1IDc4LjI0NjEgMTIyLjYyNCA3OC4yNDYxQzEyMi4xOCA3OC4yNDYxIDEyMS43ODMgNzguMzM5MiAxMjEuNDMyIDc4LjUyNTRDMTIxLjA4MSA3OC43MTE2IDEyMC43ODQgNzguOTY1OCAxMjAuNTQgNzkuMjg4MUMxMjAuMzA0IDc5LjYxMDQgMTIwLjEyMSA3OS45ODI3IDExOS45OTIgODAuNDA1M0MxMTkuODYzIDgwLjgyNzggMTE5Ljc5OSA4MS4yNzkgMTE5Ljc5OSA4MS43NTg4Wk0xMzcuMjc5IDg1LjU5MzhWNzEuNUgxMzkuODc5Vjg4SDEzNy41MjZMMTM3LjI3OSA4NS41OTM4Wk0xMjkuNzE3IDgyLjMxNzRWODIuMDkxOEMxMjkuNzE3IDgxLjIxMDkgMTI5LjgyMSA4MC40MDg5IDEzMC4wMjggNzkuNjg1NUMxMzAuMjM2IDc4Ljk1NTEgMTMwLjUzNyA3OC4zMjg1IDEzMC45MzEgNzcuODA1N0MxMzEuMzI1IDc3LjI3NTcgMTMxLjgwNCA3Ni44NzExIDEzMi4zNyA3Ni41OTE4QzEzMi45MzYgNzYuMzA1MyAxMzMuNTczIDc2LjE2MjEgMTM0LjI4MiA3Ni4xNjIxQzEzNC45ODQgNzYuMTYyMSAxMzUuNiA3Ni4yOTgyIDEzNi4xMyA3Ni41NzAzQzEzNi42NiA3Ni44NDI0IDEzNy4xMTEgNzcuMjMyNyAxMzcuNDgzIDc3Ljc0MTJDMTM3Ljg1NiA3OC4yNDI1IDEzOC4xNTMgNzguODQ0MSAxMzguMzc1IDc5LjU0NTlDMTM4LjU5NyA4MC4yNDA2IDEzOC43NTUgODEuMDE0IDEzOC44NDggODEuODY2MlY4Mi41ODU5QzEzOC43NTUgODMuNDE2NyAxMzguNTk3IDg0LjE3NTggMTM4LjM3NSA4NC44NjMzQzEzOC4xNTMgODUuNTUwOCAxMzcuODU2IDg2LjE0NTIgMTM3LjQ4MyA4Ni42NDY1QzEzNy4xMTEgODcuMTQ3OCAxMzYuNjU2IDg3LjUzNDUgMTM2LjExOSA4Ny44MDY2QzEzNS41ODkgODguMDc4OCAxMzQuOTcgODguMjE0OCAxMzQuMjYxIDg4LjIxNDhDMTMzLjU1OSA4OC4yMTQ4IDEzMi45MjUgODguMDY4IDEzMi4zNTkgODcuNzc0NEMxMzEuODAxIDg3LjQ4MDggMTMxLjMyNSA4Ny4wNjkgMTMwLjkzMSA4Ni41MzkxQzEzMC41MzcgODYuMDA5MSAxMzAuMjM2IDg1LjM4NjEgMTMwLjAyOCA4NC42Njk5QzEyOS44MjEgODMuOTQ2NiAxMjkuNzE3IDgzLjE2MjQgMTI5LjcxNyA4Mi4zMTc0Wk0xMzIuMzA2IDgyLjA5MThWODIuMzE3NEMxMzIuMzA2IDgyLjg0NzMgMTMyLjM1MiA4My4zNDE1IDEzMi40NDUgODMuNzk5OEMxMzIuNTQ2IDg0LjI1ODEgMTMyLjcgODQuNjYyOCAxMzIuOTA3IDg1LjAxMzdDMTMzLjExNSA4NS4zNTc0IDEzMy4zODMgODUuNjI5NiAxMzMuNzEzIDg1LjgzMDFDMTM0LjA0OSA4Ni4wMjM0IDEzNC40NTEgODYuMTIwMSAxMzQuOTE2IDg2LjEyMDFDMTM1LjUwMyA4Ni4xMjAxIDEzNS45ODcgODUuOTkxMiAxMzYuMzY2IDg1LjczMzRDMTM2Ljc0NiA4NS40NzU2IDEzNy4wNDMgODUuMTI4MyAxMzcuMjU4IDg0LjY5MTRDMTM3LjQ4IDg0LjI0NzQgMTM3LjYzIDgzLjc1MzMgMTM3LjcwOSA4My4yMDlWODEuMjY0NkMxMzcuNjY2IDgwLjg0MjEgMTM3LjU3NiA4MC40NDgyIDEzNy40NCA4MC4wODNDMTM3LjMxMiA3OS43MTc4IDEzNy4xMzYgNzkuMzk5MSAxMzYuOTE0IDc5LjEyN0MxMzYuNjkyIDc4Ljg0NzcgMTM2LjQxNiA3OC42MzI4IDEzNi4wODcgNzguNDgyNEMxMzUuNzY1IDc4LjMyNDkgMTM1LjM4MiA3OC4yNDYxIDEzNC45MzggNzguMjQ2MUMxMzQuNDY1IDc4LjI0NjEgMTM0LjA2NCA3OC4zNDY0IDEzMy43MzQgNzguNTQ2OUMxMzMuNDA1IDc4Ljc0NzQgMTMzLjEzMyA3OS4wMjMxIDEzMi45MTggNzkuMzc0QzEzMi43MSA3OS43MjQ5IDEzMi41NTYgODAuMTMzMSAxMzIuNDU2IDgwLjU5ODZDMTMyLjM1NiA4MS4wNjQxIDEzMi4zMDYgODEuNTYxOCAxMzIuMzA2IDgyLjA5MThaIiBmaWxsPSIjM0Y1MkREIi8+Cjwvc3ZnPgo=", - "description": "Sends the command to the device or updates attribute/time-series when the user clicks the button. Widget settings will enable you to configure behavior how to fetch the initial state and what to trigger when click.", + "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, @@ -31,6 +31,9 @@ "subroutine call", "inter-process communication", "server request", - "button" + "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/single_switch.json b/application/src/main/data/json/system/widget_types/single_switch.json index b14cf633a2..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 @@ -3,7 +3,7 @@ "name": "Single Switch", "deprecated": false, "image": "tb-image:c2luZ2xlLXN3aXRjaC5zdmc=:IlNpbmdsZSBTd2l0Y2giIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMzEuNTc3MSIgeT0iNjUuMDc0MiIgd2lkdGg9IjU2Ljk5MDEiIGhlaWdodD0iMjkuODUyIiByeD0iMTQuOTI2IiBmaWxsPSIjNTQ2OUZGIi8+CjxjaXJjbGUgY3g9IjczLjY0MDkiIGN5PSI4MC4wMDAzIiByPSIxMi4yMTIyIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTA5LjI0MSA4My40NzE3QzEwOS4yNDEgODMuMTQ5NCAxMDkuMTkxIDgyLjg2MyAxMDkuMDkxIDgyLjYxMjNDMTA4Ljk5OCA4Mi4zNjE3IDEwOC44MjkgODIuMTMyNSAxMDguNTg2IDgxLjkyNDhDMTA4LjM0MiA4MS43MTcxIDEwNy45OTkgODEuNTE2NiAxMDcuNTU1IDgxLjMyMzJDMTA3LjExOCA4MS4xMjI3IDEwNi41NTkgODAuOTE4NiAxMDUuODc5IDgwLjcxMDlDMTA1LjEzNCA4MC40ODE4IDEwNC40NDcgODAuMjI3NSAxMDMuODE2IDc5Ljk0ODJDMTAzLjE5MyA3OS42NjE4IDEwMi42NDkgNzkuMzMyNCAxMDIuMTg0IDc4Ljk2QzEwMS43MTggNzguNTgwNCAxMDEuMzU2IDc4LjE0NzEgMTAxLjA5OSA3Ny42NjAyQzEwMC44NDEgNzcuMTY2IDEwMC43MTIgNzYuNTk2NyAxMDAuNzEyIDc1Ljk1MjFDMTAwLjcxMiA3NS4zMTQ4IDEwMC44NDQgNzQuNzM0NyAxMDEuMTA5IDc0LjIxMTlDMTAxLjM4MiA3My42ODkxIDEwMS43NjUgNzMuMjM4IDEwMi4yNTkgNzIuODU4NEMxMDIuNzYgNzIuNDcxNyAxMDMuMzUxIDcyLjE3NDUgMTA0LjAzMSA3MS45NjY4QzEwNC43MTIgNzEuNzUyIDEwNS40NjQgNzEuNjQ0NSAxMDYuMjg3IDcxLjY0NDVDMTA3LjQ0NyA3MS42NDQ1IDEwOC40NDYgNzEuODU5NCAxMDkuMjg0IDcyLjI4OTFDMTEwLjEyOSA3Mi43MTg4IDExMC43NzcgNzMuMjk1MiAxMTEuMjI5IDc0LjAxODZDMTExLjY4NyA3NC43NDE5IDExMS45MTYgNzUuNTQwNCAxMTEuOTE2IDc2LjQxNDFIMTA5LjI0MUMxMDkuMjQxIDc1Ljg5ODQgMTA5LjEzIDc1LjQ0MzcgMTA4LjkwOCA3NS4wNDk4QzEwOC42OTMgNzQuNjQ4OCAxMDguMzY0IDc0LjMzMzcgMTA3LjkyIDc0LjEwNDVDMTA3LjQ4MyA3My44NzUzIDEwNi45MjggNzMuNzYwNyAxMDYuMjU1IDczLjc2MDdDMTA1LjYxOCA3My43NjA3IDEwNS4wODggNzMuODU3NCAxMDQuNjY1IDc0LjA1MDhDMTA0LjI0MyA3NC4yNDQxIDEwMy45MjcgNzQuNTA1NSAxMDMuNzIgNzQuODM1QzEwMy41MTIgNzUuMTY0NCAxMDMuNDA4IDc1LjUzNjggMTAzLjQwOCA3NS45NTIxQzEwMy40MDggNzYuMjQ1OCAxMDMuNDc2IDc2LjUxNDMgMTAzLjYxMiA3Ni43NTc4QzEwMy43NDggNzYuOTk0MSAxMDMuOTU2IDc3LjIxNjEgMTA0LjIzNSA3Ny40MjM4QzEwNC41MTUgNzcuNjI0MyAxMDQuODY2IDc3LjgxNDEgMTA1LjI4OCA3Ny45OTMyQzEwNS43MTEgNzguMTcyMiAxMDYuMjA4IDc4LjM0NDEgMTA2Ljc4MSA3OC41MDg4QzEwNy42NDggNzguNzY2NiAxMDguNDAzIDc5LjA1MzEgMTA5LjA0OCA3OS4zNjgyQzEwOS42OTIgNzkuNjc2MSAxMTAuMjI5IDgwLjAyNyAxMTAuNjU5IDgwLjQyMDlDMTExLjA4OSA4MC44MTQ4IDExMS40MTEgODEuMjYyNCAxMTEuNjI2IDgxLjc2MzdDMTExLjg0MSA4Mi4yNTc4IDExMS45NDggODIuODIgMTExLjk0OCA4My40NTAyQzExMS45NDggODQuMTA5IDExMS44MTYgODQuNzAzNSAxMTEuNTUxIDg1LjIzMzRDMTExLjI4NiA4NS43NTYyIDExMC45MDYgODYuMjAzOCAxMTAuNDEyIDg2LjU3NjJDMTA5LjkyNSA4Ni45NDE0IDEwOS4zMzggODcuMjI0MyAxMDguNjUgODcuNDI0OEMxMDcuOTcgODcuNjE4MiAxMDcuMjExIDg3LjcxNDggMTA2LjM3MyA4Ny43MTQ4QzEwNS42MjEgODcuNzE0OCAxMDQuODggODcuNjE0NiAxMDQuMTQ5IDg3LjQxNDFDMTAzLjQyNiA4Ny4yMTM1IDEwMi43NjcgODYuOTA5MiAxMDIuMTczIDg2LjUwMUMxMDEuNTc4IDg2LjA4NTYgMTAxLjEwNiA4NS41NyAxMDAuNzU1IDg0Ljk1NDFDMTAwLjQwNCA4NC4zMzExIDEwMC4yMjkgODMuNjA0MiAxMDAuMjI5IDgyLjc3MzRIMTAyLjkyNUMxMDIuOTI1IDgzLjI4MTkgMTAzLjAxMSA4My43MTUyIDEwMy4xODMgODQuMDczMkMxMDMuMzYyIDg0LjQzMTMgMTAzLjYwOSA4NC43MjQ5IDEwMy45MjQgODQuOTU0MUMxMDQuMjM5IDg1LjE3NjEgMTA0LjYwNCA4NS4zNDA4IDEwNS4wMiA4NS40NDgyQzEwNS40NDIgODUuNTU1NyAxMDUuODkzIDg1LjYwOTQgMTA2LjM3MyA4NS42MDk0QzEwNy4wMDMgODUuNjA5NCAxMDcuNTMgODUuNTE5OSAxMDcuOTUyIDg1LjM0MDhDMTA4LjM4MiA4NS4xNjE4IDEwOC43MDQgODQuOTExMSAxMDguOTE5IDg0LjU4ODlDMTA5LjEzNCA4NC4yNjY2IDEwOS4yNDEgODMuODk0MiAxMDkuMjQxIDgzLjQ3MTdaTTExNy41NzcgODQuOTIxOUwxMjAuMjYzIDc1Ljg3N0gxMjEuOTE3TDEyMS40NjYgNzguNTg0TDExOC43NTkgODcuNUgxMTcuMjc2TDExNy41NzcgODQuOTIxOVpNMTE1Ljk5OCA3NS44NzdMMTE4LjA5MyA4NC45NjQ4TDExOC4yNjUgODcuNUgxMTYuNjFMMTEzLjQ2MyA3NS44NzdIMTE1Ljk5OFpNMTI0LjQzMSA4NC44NTc0TDEyNi40NjEgNzUuODc3SDEyOC45ODVMMTI1Ljg0OSA4Ny41SDEyNC4xOTRMMTI0LjQzMSA4NC44NTc0Wk0xMjIuMTk2IDc1Ljg3N0wxMjQuODUgODQuODE0NUwxMjUuMTgzIDg3LjVIMTIzLjdMMTIwLjk2MSA3OC41NzMyTDEyMC41MSA3NS44NzdIMTIyLjE5NlpNMTMzLjg2MiA3NS44NzdWODcuNUgxMzEuMjYzVjc1Ljg3N0gxMzMuODYyWk0xMzEuMDkxIDcyLjgyNjJDMTMxLjA5MSA3Mi40MzIzIDEzMS4yMiA3Mi4xMDY0IDEzMS40NzggNzEuODQ4NkMxMzEuNzQzIDcxLjU4MzcgMTMyLjEwOCA3MS40NTEyIDEzMi41NzMgNzEuNDUxMkMxMzMuMDMyIDcxLjQ1MTIgMTMzLjM5MyA3MS41ODM3IDEzMy42NTggNzEuODQ4NkMxMzMuOTIzIDcyLjEwNjQgMTM0LjA1NiA3Mi40MzIzIDEzNC4wNTYgNzIuODI2MkMxMzQuMDU2IDczLjIxMjkgMTMzLjkyMyA3My41MzUyIDEzMy42NTggNzMuNzkzQzEzMy4zOTMgNzQuMDUwOCAxMzMuMDMyIDc0LjE3OTcgMTMyLjU3MyA3NC4xNzk3QzEzMi4xMDggNzQuMTc5NyAxMzEuNzQzIDc0LjA1MDggMTMxLjQ3OCA3My43OTNDMTMxLjIyIDczLjUzNTIgMTMxLjA5MSA3My4yMTI5IDEzMS4wOTEgNzIuODI2MlpNMTQyLjM3IDc1Ljg3N1Y3Ny43Njc2SDEzNS44MTdWNzUuODc3SDE0Mi4zN1pNMTM3LjcwOCA3My4wMzAzSDE0MC4yOTdWODQuMjg4MUMxNDAuMjk3IDg0LjY0NjIgMTQwLjM0NyA4NC45MjE5IDE0MC40NDcgODUuMTE1MkMxNDAuNTU1IDg1LjMwMTQgMTQwLjcwMSA4NS40MjY4IDE0MC44ODggODUuNDkxMkMxNDEuMDc0IDg1LjU1NTcgMTQxLjI5MiA4NS41ODc5IDE0MS41NDMgODUuNTg3OUMxNDEuNzIyIDg1LjU4NzkgMTQxLjg5NCA4NS41NzcxIDE0Mi4wNTkgODUuNTU1N0MxNDIuMjIzIDg1LjUzNDIgMTQyLjM1NiA4NS41MTI3IDE0Mi40NTYgODUuNDkxMkwxNDIuNDY3IDg3LjQ2NzhDMTQyLjI1MiA4Ny41MzIyIDE0Mi4wMDEgODcuNTg5NSAxNDEuNzE1IDg3LjYzOTZDMTQxLjQzNiA4Ny42ODk4IDE0MS4xMTMgODcuNzE0OCAxNDAuNzQ4IDg3LjcxNDhDMTQwLjE1NCA4Ny43MTQ4IDEzOS42MjcgODcuNjExIDEzOS4xNjkgODcuNDAzM0MxMzguNzExIDg3LjE4ODUgMTM4LjM1MyA4Ni44NDExIDEzOC4wOTUgODYuMzYxM0MxMzcuODM3IDg1Ljg4MTUgMTM3LjcwOCA4NS4yNDQxIDEzNy43MDggODQuNDQ5MlY3My4wMzAzWk0xNDkuNDYgODUuNjUyM0MxNDkuODgyIDg1LjY1MjMgMTUwLjI2MiA4NS41NyAxNTAuNTk5IDg1LjQwNTNDMTUwLjk0MiA4NS4yMzM0IDE1MS4yMTggODQuOTk3MSAxNTEuNDI2IDg0LjY5NjNDMTUxLjY0MSA4NC4zOTU1IDE1MS43NTkgODQuMDQ4MiAxNTEuNzggODMuNjU0M0gxNTQuMjE5QzE1NC4yMDQgODQuNDA2MiAxNTMuOTgyIDg1LjA5MDIgMTUzLjU1MyA4NS43MDYxQzE1My4xMjMgODYuMzIxOSAxNTIuNTU0IDg2LjgxMjUgMTUxLjg0NSA4Ny4xNzc3QzE1MS4xMzYgODcuNTM1OCAxNTAuMzUyIDg3LjcxNDggMTQ5LjQ5MiA4Ny43MTQ4QzE0OC42MDQgODcuNzE0OCAxNDcuODMxIDg3LjU2NDUgMTQ3LjE3MiA4Ny4yNjM3QzE0Ni41MTMgODYuOTU1NyAxNDUuOTY1IDg2LjUzMzIgMTQ1LjUyOCA4NS45OTYxQzE0NS4wOTEgODUuNDU5IDE0NC43NjIgODQuODM5NSAxNDQuNTQgODQuMTM3N0MxNDQuMzI1IDgzLjQzNTkgMTQ0LjIxOCA4Mi42ODM5IDE0NC4yMTggODEuODgxOFY4MS41MDU5QzE0NC4yMTggODAuNzAzOCAxNDQuMzI1IDc5Ljk1MTggMTQ0LjU0IDc5LjI1QzE0NC43NjIgNzguNTQxIDE0NS4wOTEgNzcuOTE4IDE0NS41MjggNzcuMzgwOUMxNDUuOTY1IDc2Ljg0MzggMTQ2LjUxMyA3Ni40MjQ4IDE0Ny4xNzIgNzYuMTI0QzE0Ny44MzEgNzUuODE2MSAxNDguNjAxIDc1LjY2MjEgMTQ5LjQ4MSA3NS42NjIxQzE1MC40MTIgNzUuNjYyMSAxNTEuMjI5IDc1Ljg0ODMgMTUxLjkzMSA3Ni4yMjA3QzE1Mi42MzIgNzYuNTg1OSAxNTMuMTg0IDc3LjA5OCAxNTMuNTg1IDc3Ljc1NjhDMTUzLjk5MyA3OC40MDg1IDE1NC4yMDQgNzkuMTY3NiAxNTQuMjE5IDgwLjAzNDJIMTUxLjc4QzE1MS43NTkgNzkuNjA0NSAxNTEuNjUxIDc5LjIxNzggMTUxLjQ1OCA3OC44NzRDMTUxLjI3MiA3OC41MjMxIDE1MS4wMDcgNzguMjQzOCAxNTAuNjYzIDc4LjAzNjFDMTUwLjMyNiA3Ny44Mjg1IDE0OS45MjIgNzcuNzI0NiAxNDkuNDQ5IDc3LjcyNDZDMTQ4LjkyNiA3Ny43MjQ2IDE0OC40OTMgNzcuODMyIDE0OC4xNDkgNzguMDQ2OUMxNDcuODA2IDc4LjI1NDYgMTQ3LjUzNyA3OC41NDEgMTQ3LjM0NCA3OC45MDYyQzE0Ny4xNSA3OS4yNjQzIDE0Ny4wMTEgNzkuNjY4OSAxNDYuOTI1IDgwLjEyMDFDMTQ2Ljg0NiA4MC41NjQxIDE0Ni44MDcgODEuMDI2IDE0Ni44MDcgODEuNTA1OVY4MS44ODE4QzE0Ni44MDcgODIuMzYxNyAxNDYuODQ2IDgyLjgyNzEgMTQ2LjkyNSA4My4yNzgzQzE0Ny4wMDQgODMuNzI5NSAxNDcuMTQgODQuMTM0MSAxNDcuMzMzIDg0LjQ5MjJDMTQ3LjUzNCA4NC44NDMxIDE0Ny44MDYgODUuMTI2IDE0OC4xNDkgODUuMzQwOEMxNDguNDkzIDg1LjU0ODUgMTQ4LjkzIDg1LjY1MjMgMTQ5LjQ2IDg1LjY1MjNaTTE1OS4xMDYgNzFWODcuNUgxNTYuNTI4VjcxSDE1OS4xMDZaTTE1OC42NTUgODEuMjU4OEwxNTcuODE3IDgxLjI0OEMxNTcuODI1IDgwLjQ0NiAxNTcuOTM2IDc5LjcwNDggMTU4LjE1IDc5LjAyNDRDMTU4LjM3MiA3OC4zNDQxIDE1OC42OCA3Ny43NTMzIDE1OS4wNzQgNzcuMjUyQzE1OS40NzUgNzYuNzQzNSAxNTkuOTU1IDc2LjM1MzIgMTYwLjUxNCA3Ni4wODExQzE2MS4wNzIgNzUuODAxOCAxNjEuNjkyIDc1LjY2MjEgMTYyLjM3MiA3NS42NjIxQzE2Mi45NDUgNzUuNjYyMSAxNjMuNDYxIDc1Ljc0MDkgMTYzLjkxOSA3NS44OTg0QzE2NC4zODQgNzYuMDU2IDE2NC43ODUgNzYuMzEwMiAxNjUuMTIyIDc2LjY2MTFDMTY1LjQ1OSA3Ny4wMDQ5IDE2NS43MTMgNzcuNDU2MSAxNjUuODg1IDc4LjAxNDZDMTY2LjA2NCA3OC41NjYxIDE2Ni4xNTMgNzkuMjM5MyAxNjYuMTUzIDgwLjAzNDJWODcuNUgxNjMuNTU0VjgwLjAxMjdDMTYzLjU1NCA3OS40NTQxIDE2My40NzEgNzkuMDEwMSAxNjMuMzA3IDc4LjY4MDdDMTYzLjE0OSA3OC4zNTEyIDE2Mi45MTYgNzguMTE0OSAxNjIuNjA4IDc3Ljk3MTdDMTYyLjMgNzcuODIxMyAxNjEuOTI0IDc3Ljc0NjEgMTYxLjQ4IDc3Ljc0NjFDMTYxLjAxNSA3Ny43NDYxIDE2MC42MDMgNzcuODM5MiAxNjAuMjQ1IDc4LjAyNTRDMTU5Ljg5NCA3OC4yMTE2IDE1OS42MDEgNzguNDY1OCAxNTkuMzY0IDc4Ljc4ODFDMTU5LjEyOCA3OS4xMTA0IDE1OC45NDkgNzkuNDgyNyAxNTguODI3IDc5LjkwNTNDMTU4LjcxMyA4MC4zMjc4IDE1OC42NTUgODAuNzc5IDE1OC42NTUgODEuMjU4OFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNzYiLz4KPC9zdmc+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.", + "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, @@ -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/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 4160b50024..92e4ab9895 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5185,7 +5185,7 @@ "action-button": { "behavior": "Behavior", "on-click": "On click", - "on-click-hint": "Action performed when the button is clicked." + "on-click-hint": "Action triggered when the button is clicked" }, "command-button": { "behavior": "Behavior", @@ -5214,9 +5214,9 @@ }, "button-state": { "activated-state": "Activated state", - "activated-state-hint": "Condition under which the button is active.", + "activated-state-hint": "Configure condition under which the button is active.", "disabled-state": "Disabled state", - "disabled-state-hint": "Condition under which the button is disabled.", + "disabled-state-hint": "Configure condition under which the button is disabled.", "enabled": "Enabled", "hovered": "Hovered", "pressed": "Pressed", @@ -6526,13 +6526,13 @@ }, "rpc-state": { "initial-state": "Initial state", - "initial-state-hint": "Action to get the initial value of the component.", + "initial-state-hint": "Action to get the initial state (On/Off) of the component.", "disabled-state": "Disabled state", - "disabled-state-hint": "Condition under which the component is disabled.", + "disabled-state-hint": "Configure condition under which the component is disabled.", "turn-on": "Turn 'On'", - "turn-on-hint": "Action performed to turn ON the component.", + "turn-on-hint": "Action triggered when the slider is switched to 'On'", "turn-off": "Turn 'Off'", - "turn-off-hint": "Action performed to turn OFF the component.", + "turn-off-hint": "Action triggered when the slider is switched to 'Off'", "on": "On", "off": "Off", "disabled": "Disabled" From 7cd4d477ff98d66072ebca833afda3b596f9a0d5 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 8 Feb 2024 15:53:55 +0200 Subject: [PATCH 112/128] Fix edge cycling: update edge root rule chain, add test. Improve Edge rpc request to edge device. Add support for originator fields for rule nodes --- .../device/DeviceActorMessageProcessor.java | 15 ++-- .../RuleChainActorMessageProcessor.java | 2 +- .../service/edge/rpc/EdgeGrpcService.java | 2 + .../rule/RuleChainEdgeProcessor.java | 5 ++ .../service/queue/TbMsgPackCallback.java | 1 - .../server/edge/RuleChainEdgeTest.java | 23 ++++- .../RuleChainMsgConstructorTest.java | 87 +++++++++---------- .../notification/DefaultNotifications.java | 4 +- .../thingsboard/rest/client/RestClient.java | 5 +- .../util/EntitiesFieldsAsyncLoader.java | 4 + 10 files changed, 89 insertions(+), 59 deletions(-) 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..9f92838712 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,12 @@ 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; + Optional edgeAttributeOpt = systemContext.getAttributesService().find(tenantId, edgeId, DataConstants.SERVER_SCOPE, DefaultDeviceStateService.ACTIVITY_STATE).get(); + if (edgeAttributeOpt.isPresent() && edgeAttributeOpt.get().getBooleanValue().orElse(false)) { + 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 +475,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/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index c23fd98d8e..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 @@ -477,6 +477,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i 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); 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/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/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/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/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 1ef0006e87..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,9 +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.EdgeCommunicationFailureNotificationRuleTriggerConfig; 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; @@ -347,7 +347,7 @@ public class DefaultNotifications { public static final DefaultNotification edgeCommunicationFailures = DefaultNotification.builder() .name("Edge communication failure notification") .type(NotificationType.EDGE_COMMUNICATION_FAILURE) - .subject("Edge '${edgeName}' communication failure occured") + .subject("Edge '${edgeName}' communication failure occurred") .text("Failure message: '${failureMsg}'") .icon("error").color(RED_COLOR) .button("Go to Edge").link("/edgeManagement/instances/${edgeId}") 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/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())); } From 841514384209e5a976e9b3c64d53cd1d99de4fb2 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 8 Feb 2024 16:51:49 +0200 Subject: [PATCH 113/128] Add TO_SERVER_RPC_REQUEST to AbstractTbMsgPushNode --- .../thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 Date: Thu, 8 Feb 2024 18:09:12 +0200 Subject: [PATCH 114/128] UI: added new open URL action type --- .../action/widget-action.component.html | 19 ++++++++++++++++++- .../common/action/widget-action.component.ts | 10 ++++++++++ .../components/widget/widget.component.ts | 3 +++ ui-ngx/src/app/shared/models/widget.models.ts | 7 +++++-- .../assets/locale/locale.constant-en_US.json | 3 +++ 5 files changed, 39 insertions(+), 3 deletions(-) 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 index 35ca3f3144..3752fb3b3c 100644 --- 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 @@ -80,6 +80,22 @@
+ +
+
{{ 'widget-action.URL' | translate }}
+ + + + warning + + +
+
@@ -89,7 +105,8 @@
-
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 index c93a05fafd..6c2cd3fb8f 100644 --- 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 @@ -279,6 +279,16 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali 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( 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 15e982c060..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 @@ -1089,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) { diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 625d957cff..5f4ca8f7be 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -538,7 +538,8 @@ export enum WidgetActionType { openDashboard = 'openDashboard', custom = 'custom', customPretty = 'customPretty', - mobileAction = 'mobileAction' + mobileAction = 'mobileAction', + openURL = 'openURL' } export enum WidgetMobileActionType { @@ -559,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' ] ] ); @@ -671,6 +673,7 @@ export interface WidgetAction extends CustomActionDescriptor { setEntityId?: boolean; stateEntityParamName?: string; mobileAction?: WidgetMobileActionDescriptor; + url?: string; } export interface WidgetActionDescriptor extends WidgetAction { 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 b3d2821e30..ee3d1d29d7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5036,6 +5036,9 @@ "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", From 2faf63bf81c82a6e352015c82e311d7b36d12335 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 8 Feb 2024 19:48:32 +0200 Subject: [PATCH 115/128] UI: Implement power button widget --- .../json/system/widget_bundles/buttons.json | 3 +- .../widget_bundles/control_widgets.json | 2 + .../system/widget_types/power_button.json | 36 + .../basic/basic-widget-config.module.ts | 12 +- .../power-button-basic-config.component.html | 197 ++++ .../power-button-basic-config.component.ts | 195 ++++ .../widget/lib/action/action-widget.models.ts | 2 + .../rpc/power-button-widget.component.html | 32 + .../rpc/power-button-widget.component.scss | 64 ++ .../lib/rpc/power-button-widget.component.ts | 200 ++++ .../lib/rpc/power-button-widget.models.ts | 912 ++++++++++++++++++ ...ower-button-widget-settings.component.html | 142 +++ .../power-button-widget-settings.component.ts | 88 ++ .../lib/settings/widget-settings.module.ts | 12 +- .../widget/widget-components.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 22 + .../widget/power-button/default-layout.svg | 22 + .../power-button/default-volume-layout.svg | 50 + .../widget/power-button/outlined-layout.svg | 23 + .../power-button/outlined-volume-layout.svg | 37 + .../widget/power-button/simplified-layout.svg | 20 + .../power-button/simplified-volume-layout.svg | 33 + 22 files changed, 2102 insertions(+), 9 deletions(-) create mode 100644 application/src/main/data/json/system/widget_types/power_button.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/power-button-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.ts create mode 100644 ui-ngx/src/assets/widget/power-button/default-layout.svg create mode 100644 ui-ngx/src/assets/widget/power-button/default-volume-layout.svg create mode 100644 ui-ngx/src/assets/widget/power-button/outlined-layout.svg create mode 100644 ui-ngx/src/assets/widget/power-button/outlined-volume-layout.svg create mode 100644 ui-ngx/src/assets/widget/power-button/simplified-layout.svg create mode 100644 ui-ngx/src/assets/widget/power-button/simplified-volume-layout.svg diff --git a/application/src/main/data/json/system/widget_bundles/buttons.json b/application/src/main/data/json/system/widget_bundles/buttons.json index a48fba892b..00036a0cba 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -9,6 +9,7 @@ }, "widgetTypeFqns": [ "action_button", - "command_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..568a144874 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,8 @@ }, "widgetTypeFqns": [ "single_switch", + "command_button", + "power_button", "control_widgets.switch_control", "control_widgets.slide_toggle_control", "control_widgets.round_switch", 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..2f825c9e74 --- /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\":{\"initialState\":{\"action\":\"EXECUTE_RPC\",\"defaultValue\":false,\"executeRpc\":{\"method\":\"getState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"onUpdateState\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SHARED_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"CONSTANT\",\"constantValue\":true,\"valueToDataFunction\":\"/* Convert input boolean value to RPC parameters or attribute/time-series value */\\nreturn value;\"}},\"offUpdateState\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SHARED_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"CONSTANT\",\"constantValue\":false,\"valueToDataFunction\":\"/* Convert input boolean value to RPC parameters or attribute/time-series value */ \\n return value;\"}},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"layout\":\"default\",\"mainColorOn\":\"#3F52DD\",\"backgroundColorOn\":\"#FFFFFF\",\"mainColorOff\":\"#A2A2A2\",\"backgroundColorOff\":\"#FFFFFF\",\"mainColorDisabled\":\"rgba(0,0,0,0.12)\",\"backgroundColorDisabled\":\"#FFFFFF\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"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/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index bb31daf7bb..c268be7bcd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -100,6 +100,9 @@ import { import { CommandButtonBasicConfigComponent } from '@home/components/widget/config/basic/button/command-button-basic-config.component'; +import { + PowerButtonBasicConfigComponent +} from '@home/components/widget/config/basic/button/power-button-basic-config.component'; @NgModule({ declarations: [ @@ -131,7 +134,8 @@ import { BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, - CommandButtonBasicConfigComponent + CommandButtonBasicConfigComponent, + PowerButtonBasicConfigComponent ], imports: [ CommonModule, @@ -167,7 +171,8 @@ import { BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, - CommandButtonBasicConfigComponent + CommandButtonBasicConfigComponent, + PowerButtonBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -197,5 +202,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type + + +
+
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/lib/action/action-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts index 26dc76866a..dc56a1e992 100644 --- 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 @@ -91,6 +91,8 @@ export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, A ngOnDestroy() { this.valueActions.forEach(v => v.destroy()); + this.loadingSubject.complete(); + this.loadingSubject.unsubscribe(); } public onInit() { 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..93e394ac5e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.html @@ -0,0 +1,32 @@ + +
+
+ +
+
+
+
+ +
+
+
+ +
+
+
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..e0192630f9 --- /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 4px 6px rgba(0, 0, 0, 0.08)); + } + .tb-shadow { + filter: drop-shadow(8px 8px 8px rgba(0, 0, 0, 0.1)); + } + } + &.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..e68a2e4baa --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts @@ -0,0 +1,200 @@ +/// +/// 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); + }); + } + + 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); + } + } + + private onDisabled(value: boolean): void { + const newDisabled = !!value; + if (this.disabled !== newDisabled) { + this.disabled = newDisabled; + this.updateDisabledState(this.disabled); + } + } + + 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..b1372a9660 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -0,0 +1,912 @@ +/// +/// 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, Element, G, Gradient, Runner, Stop, Svg, Text, Timeline } from '@svgdotjs/svg.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; + +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 createPressedShadow(diameter: number, rightElseLeft = true): Gradient { + const innerShadowGradient = this.svgShape.gradient('radial', (add) => { + add.stop(0.7, '#000000', 0); + add.stop(1, '#000000', 0.4); + }).attr({ cx: rightElseLeft ? '45%' : '55%', cy: '55%', r: '100%'}); + this.svgShape.circle(diameter).center(cx, cy) + .fill(innerShadowGradient).stroke({width: 0}); + return innerShadowGradient; + } + + protected pressedAnimation(element: Element): Runner { + return element.animate(200, 0, 'now'); + } + + 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 DefaultPowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private offLabelShape: Text; + private onCircleShape: Circle; + private onLabelShape: Text; + private pressedShadow: Gradient; + 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 = this.createPressedShadow(powerButtonShapeSize - 20); + + 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; + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + } + +} + +class SimplifiedPowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private onCircleShape: Circle; + private offLabelShape: Text; + private onLabelShape: Text; + private pressedShadow: Gradient; + 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 = this.createPressedShadow(powerButtonShapeSize - 4); + + 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; + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + } +} + +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: Gradient; + 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 = this.createPressedShadow(powerButtonShapeSize - 24); + + 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; + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onCenterGroup).transform({scale: 0.98}); + this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); + this.pressedAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + } +} + +class DefaultVolumePowerButtonShape extends PowerButtonShape { + private outerBorder: Circle; + private outerBorderMask: Circle; + private outerBorderGradient: Gradient; + private innerBorder: Circle; + private innerBorderMask: Circle; + private innerBorderGradient: Gradient; + private innerShadow: Circle; + 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 = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); + this.innerShadowGradient = this.svgShape.gradient('radial', (add) => { + add.stop(0.7, '#000000', 0); + this.innerShadowGradientStop = add.stop(1, '#000000', 0.2); + }).attr({ cx: '45%', cy: '55%', r: '65%'}); + this.innerShadow.fill(this.innerShadowGradient); + + this.pressedTimeline = new Timeline(); + this.centerGroup.timeline(this.pressedTimeline); + this.onLabelShape.timeline(this.pressedTimeline); + this.innerShadowGradient.timeline(this.pressedTimeline); + this.innerShadowGradientStop.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; + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedAnimation(this.innerShadowGradient).attr({ r: '60%'}); + this.pressedAnimation(this.innerShadowGradientStop).update({ opacity: 0.4 }); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedAnimation(this.innerShadowGradient).attr({ r: '65%'}).after(() => { + if (this.disabled) { + this.innerShadow.hide(); + } + }); + this.pressedAnimation(this.innerShadowGradientStop).update({ opacity: 0.2 }); + } + +} + +class SimplifiedVolumePowerButtonShape extends PowerButtonShape { + + private outerBorder: Circle; + private outerBorderMask: Circle; + private offLabelShape: Text; + private onLabelShape: Text; + private innerShadow: Circle; + private pressedShadow: Gradient; + 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 = this.svgShape.circle(powerButtonShapeSize - 4).center(cx, cy); + const innerShadowGradient = this.svgShape.gradient('radial', (add) => { + add.stop(0.7, '#000000', 0); + add.stop(1, '#000000', 0.2); + }).attr({ cx: '55%', cy: '55%', r: '65%'}); + this.innerShadow.fill(innerShadowGradient); + this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 4, false); + + 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'); + } + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onCenterGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); + this.pressedAnimation(this.pressedShadow).attr({r: '100%'}).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: Gradient; + 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 = this.createPressedShadow(powerButtonShapeSize - 30); + 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; + this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); + this.pressedAnimation(this.onCenterGroup).transform({scale: 0.98}); + this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + } + + protected onPressEnd() { + this.pressedTimeline.finish(); + this.pressedAnimation(this.centerGroup).transform({scale: 1}); + this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); + this.pressedAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + } + +} 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..ced9bf9575 --- /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/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 3f4a619c49..d3fe9dac3e 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 @@ -321,6 +321,9 @@ import { 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'; @NgModule({ declarations: [ @@ -436,7 +439,8 @@ import { BarChartWithLabelsWidgetSettingsComponent, SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, - CommandButtonWidgetSettingsComponent + CommandButtonWidgetSettingsComponent, + PowerButtonWidgetSettingsComponent ], imports: [ CommonModule, @@ -557,7 +561,8 @@ import { BarChartWithLabelsWidgetSettingsComponent, SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, - CommandButtonWidgetSettingsComponent + CommandButtonWidgetSettingsComponent, + PowerButtonWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -645,5 +650,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 753f502891b8d1cfddd7d14c639b09267639bf5f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 8 Feb 2024 19:57:47 +0200 Subject: [PATCH 116/128] UI: Update power button widget default settings. --- .../src/main/data/json/system/widget_types/power_button.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 2f825c9e74..d90439d82f 100644 --- a/application/src/main/data/json/system/widget_types/power_button.json +++ b/application/src/main/data/json/system/widget_types/power_button.json @@ -17,7 +17,7 @@ "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\":{\"initialState\":{\"action\":\"EXECUTE_RPC\",\"defaultValue\":false,\"executeRpc\":{\"method\":\"getState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"onUpdateState\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SHARED_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"CONSTANT\",\"constantValue\":true,\"valueToDataFunction\":\"/* Convert input boolean value to RPC parameters or attribute/time-series value */\\nreturn value;\"}},\"offUpdateState\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SHARED_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"CONSTANT\",\"constantValue\":false,\"valueToDataFunction\":\"/* Convert input boolean value to RPC parameters or attribute/time-series value */ \\n return value;\"}},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"layout\":\"default\",\"mainColorOn\":\"#3F52DD\",\"backgroundColorOn\":\"#FFFFFF\",\"mainColorOff\":\"#A2A2A2\",\"backgroundColorOff\":\"#FFFFFF\",\"mainColorDisabled\":\"rgba(0,0,0,0.12)\",\"backgroundColorDisabled\":\"#FFFFFF\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"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}" + "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", From e0e3feb0d8708c3481c9b8c2479dd8bdb9cac462 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 9 Feb 2024 09:52:25 +0200 Subject: [PATCH 117/128] Fix tests --- .../util/EntitiesFieldsAsyncLoaderTest.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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()); } From c62a95835374ddc1dd2cd73ae4ae4380d2b2468c Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 9 Feb 2024 10:46:14 +0200 Subject: [PATCH 118/128] Improve DeviceEdgeTest --- .../server/edge/DeviceEdgeTest.java | 37 ++++++++++++++++++- .../server/edge/imitator/EdgeImitator.java | 7 ++-- 2 files changed, 38 insertions(+), 6 deletions(-) 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/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) { From 360c15710e1dea33b44bccf82a9b4f771e9ebda2 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 9 Feb 2024 13:05:40 +0200 Subject: [PATCH 119/128] UI: Add pl_PL locale --- .../assets/locale/locale.constant-en_US.json | 1 + .../assets/locale/locale.constant-pl_PL.json | 6727 +++++++++++++++++ 2 files changed, 6728 insertions(+) create mode 100644 ui-ngx/src/assets/locale/locale.constant-pl_PL.json 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 4160b50024..26521dfa0a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6859,6 +6859,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-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json new file mode 100644 index 0000000000..af6fbb26db --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -0,0 +1,6727 @@ +{ + "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", + "locales":{ + "ca_ES":"Kataloński", + "cs_CZ":"Czeski", + "da_DK":"Duński", + "de_DE":"Niemiecki", + "el_GR":"Grecki", + "en_US":"Angielski", + "es_ES":"Hiszpański", + "fa_IR":"Perski", + "fr_FR":"Francuski", + "it_IT":"Włoski", + "ja_JP":"Japoński", + "ka_GE":"Gruziński", + "ko_KR":"Koreański", + "lv_LV":"Łotewski", + "nl_BE":"Niderlandzki (Belgia)", + "pt_BR":"Portugalski (Brazylia)", + "ro_RO":"Rumuński", + "sl_SI":"Słoweński", + "tr_TR":"Turecki", + "uk_UA":"Ukraiński", + "zh_CN":"Chiński uproszczony", + "zh_TW":"Chiński tradycyjny" + } + } +} From 06cba15accc152b0ee412d3ce75af07c2c9d5ae2 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 9 Feb 2024 14:36:37 +0200 Subject: [PATCH 120/128] UI: Refactoring --- .../assets/locale/locale.constant-pl_PL.json | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index af6fbb26db..014666017c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -1704,8 +1704,8 @@ "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}})", + "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", @@ -6698,30 +6698,6 @@ "items-per-page-separator":"z" }, "language":{ - "language":"Język", - "locales":{ - "ca_ES":"Kataloński", - "cs_CZ":"Czeski", - "da_DK":"Duński", - "de_DE":"Niemiecki", - "el_GR":"Grecki", - "en_US":"Angielski", - "es_ES":"Hiszpański", - "fa_IR":"Perski", - "fr_FR":"Francuski", - "it_IT":"Włoski", - "ja_JP":"Japoński", - "ka_GE":"Gruziński", - "ko_KR":"Koreański", - "lv_LV":"Łotewski", - "nl_BE":"Niderlandzki (Belgia)", - "pt_BR":"Portugalski (Brazylia)", - "ro_RO":"Rumuński", - "sl_SI":"Słoweński", - "tr_TR":"Turecki", - "uk_UA":"Ukraiński", - "zh_CN":"Chiński uproszczony", - "zh_TW":"Chiński tradycyjny" - } + "language":"Język" } } From 6943ead99de9e43e19dcc549a77c82e51247c94a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 9 Feb 2024 19:32:58 +0200 Subject: [PATCH 121/128] UI: Power button widget pressed state improvements. --- ui-ngx/package.json | 1 + .../rpc/power-button-widget.component.scss | 4 +- .../lib/rpc/power-button-widget.component.ts | 3 + .../lib/rpc/power-button-widget.models.ts | 213 +++++++++++------- ui-ngx/yarn.lock | 9 +- 5 files changed, 141 insertions(+), 89 deletions(-) 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/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 index e0192630f9..d65341a7ba 100644 --- 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 @@ -46,10 +46,10 @@ justify-content: center; svg { .tb-small-shadow { - filter: drop-shadow(0px 4px 6px rgba(0, 0, 0, 0.08)); + filter: drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.2)); } .tb-shadow { - filter: drop-shadow(8px 8px 8px rgba(0, 0, 0, 0.1)); + filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.15)); } } &.tb-power-button-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 index e68a2e4baa..02c58b3e9a 100644 --- 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 @@ -105,6 +105,7 @@ export class PowerButtonWidgetComponent extends this.loading$.subscribe((loading) => { this.updateDisabledState(loading || this.disabled); + this.cd.markForCheck(); }); } @@ -139,6 +140,7 @@ export class PowerButtonWidgetComponent extends if (this.value !== newValue) { this.value = newValue; this.powerButtonSvgShape?.setValue(this.value); + this.cd.markForCheck(); } } @@ -147,6 +149,7 @@ export class PowerButtonWidgetComponent extends if (this.disabled !== newDisabled) { this.disabled = newDisabled; this.updateDisabledState(this.disabled); + this.cd.markForCheck(); } } 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 index b1372a9660..2e8ca0de6d 100644 --- 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 @@ -24,7 +24,8 @@ import { SetValueSettings, ValueToDataType } from '@shared/models/action-widget-settings.models'; -import { Circle, Element, G, Gradient, Runner, Stop, Svg, Text, Timeline } from '@svgdotjs/svg.js'; +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'; @@ -216,6 +217,8 @@ 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, @@ -398,20 +401,6 @@ export abstract class PowerButtonShape { shape.maskWith(mask); } - protected createPressedShadow(diameter: number, rightElseLeft = true): Gradient { - const innerShadowGradient = this.svgShape.gradient('radial', (add) => { - add.stop(0.7, '#000000', 0); - add.stop(1, '#000000', 0.4); - }).attr({ cx: rightElseLeft ? '45%' : '55%', cy: '55%', r: '100%'}); - this.svgShape.circle(diameter).center(cx, cy) - .fill(innerShadowGradient).stroke({width: 0}); - return innerShadowGradient; - } - - protected pressedAnimation(element: Element): Runner { - return element.animate(200, 0, 'now'); - } - protected createOnLabel(fontWeight = '500'): Text { return this.createLabel(this.onLabel, fontWeight); } @@ -431,6 +420,72 @@ export abstract class PowerButtonShape { } +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; @@ -438,7 +493,7 @@ class DefaultPowerButtonShape extends PowerButtonShape { private offLabelShape: Text; private onCircleShape: Circle; private onLabelShape: Text; - private pressedShadow: Gradient; + private pressedShadow: InnerShadowCircle; private pressedTimeline: Timeline; private centerGroup: G; @@ -453,7 +508,7 @@ class DefaultPowerButtonShape extends PowerButtonShape { .center(cx, cy); this.onLabelShape = this.createOnLabel(); this.createMask(this.onCircleShape, [this.onLabelShape]); - this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 20); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 20, cx, cy, 0, 0); this.pressedTimeline = new Timeline(); this.centerGroup.timeline(this.pressedTimeline); @@ -482,16 +537,16 @@ class DefaultPowerButtonShape extends PowerButtonShape { protected onPressStart() { this.pressedTimeline.finish(); const pressedScale = 0.75; - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); - this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onLabelShape).transform({scale: 1}); - this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); } } @@ -503,7 +558,7 @@ class SimplifiedPowerButtonShape extends PowerButtonShape { private onCircleShape: Circle; private offLabelShape: Text; private onLabelShape: Text; - private pressedShadow: Gradient; + private pressedShadow: InnerShadowCircle; private pressedTimeline: Timeline; private centerGroup: G; @@ -517,7 +572,7 @@ class SimplifiedPowerButtonShape extends PowerButtonShape { this.onCircleShape = this.svgShape.circle(powerButtonShapeSize).center(cx, cy); this.onLabelShape = this.createOnLabel(); this.createMask(this.onCircleShape, [this.onLabelShape]); - this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 4); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 0, 0); this.pressedTimeline = new Timeline(); this.centerGroup.timeline(this.pressedTimeline); @@ -546,16 +601,16 @@ class SimplifiedPowerButtonShape extends PowerButtonShape { protected onPressStart() { this.pressedTimeline.finish(); const pressedScale = 0.75; - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); - this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onLabelShape).transform({scale: 1}); - this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); } } @@ -567,7 +622,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { private offLabelShape: Text; private onCircleShape: Circle; private onLabelShape: Text; - private pressedShadow: Gradient; + private pressedShadow: InnerShadowCircle; private pressedTimeline: Timeline; private centerGroup: G; private onCenterGroup: G; @@ -588,7 +643,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { .addTo(this.onCenterGroup); this.onLabelShape = this.createOnLabel(); this.createMask(this.onCircleShape, [this.onLabelShape]); - this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 24); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 24, cx, cy, 0, 0); this.pressedTimeline = new Timeline(); this.centerGroup.timeline(this.pressedTimeline); @@ -617,18 +672,18 @@ class OutlinedPowerButtonShape extends PowerButtonShape { protected onPressStart() { this.pressedTimeline.finish(); const pressedScale = 0.75; - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onCenterGroup).transform({scale: 0.98}); - this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); - this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + 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(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); - this.pressedAnimation(this.onLabelShape).transform({scale: 1}); - this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.pressedShadow.animateRestore(); } } @@ -639,9 +694,9 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { private innerBorder: Circle; private innerBorderMask: Circle; private innerBorderGradient: Gradient; - private innerShadow: Circle; - private innerShadowGradient: Gradient; - private innerShadowGradientStop: Stop; + private innerShadow: InnerShadowCircle; + //private innerShadowGradient: Gradient; + //private innerShadowGradientStop: Stop; private offLabelShape: Text; private onCircleShape: Circle; private onLabelShape: Text; @@ -670,18 +725,12 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); this.onLabelShape = this.createOnLabel('400'); this.createMask(this.onCircleShape, [this.onLabelShape]); - this.innerShadow = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); - this.innerShadowGradient = this.svgShape.gradient('radial', (add) => { - add.stop(0.7, '#000000', 0); - this.innerShadowGradientStop = add.stop(1, '#000000', 0.2); - }).attr({ cx: '45%', cy: '55%', r: '65%'}); - this.innerShadow.fill(this.innerShadowGradient); + 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.innerShadowGradient.timeline(this.pressedTimeline); - this.innerShadowGradientStop.timeline(this.pressedTimeline); + this.innerShadow.timeline(this.pressedTimeline); } protected drawColorState(mainColor: PowerButtonColor){ @@ -724,22 +773,20 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); this.innerShadow.show(); const pressedScale = 0.75; - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale}); - this.pressedAnimation(this.innerShadowGradient).attr({ r: '60%'}); - this.pressedAnimation(this.innerShadowGradientStop).update({ opacity: 0.4 }); + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + this.innerShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onLabelShape).transform({scale: 1}); - this.pressedAnimation(this.innerShadowGradient).attr({ r: '65%'}).after(() => { + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + this.innerShadow.animateRestore().after(() => { if (this.disabled) { this.innerShadow.hide(); } }); - this.pressedAnimation(this.innerShadowGradientStop).update({ opacity: 0.2 }); } } @@ -750,8 +797,8 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { private outerBorderMask: Circle; private offLabelShape: Text; private onLabelShape: Text; - private innerShadow: Circle; - private pressedShadow: Gradient; + private innerShadow: InnerShadowCircle; + private pressedShadow: InnerShadowCircle; private pressedTimeline: Timeline; private centerGroup: G; private onCenterGroup: G; @@ -766,14 +813,8 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); this.onCenterGroup = this.svgShape.group(); this.onLabelShape = this.createOnLabel().addTo(this.onCenterGroup); - this.innerShadow = this.svgShape.circle(powerButtonShapeSize - 4).center(cx, cy); - const innerShadowGradient = this.svgShape.gradient('radial', (add) => { - add.stop(0.7, '#000000', 0); - add.stop(1, '#000000', 0.2); - }).attr({ cx: '55%', cy: '55%', r: '65%'}); - this.innerShadow.fill(innerShadowGradient); - this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 4, false); - + 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); @@ -807,16 +848,16 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { if (!this.value) { this.backgroundShape.removeClass('tb-shadow'); } - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onCenterGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale}); + this.pressedShadow.animate(8, 0.4); } protected onPressEnd() { this.pressedTimeline.finish(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); - this.pressedAnimation(this.pressedShadow).attr({r: '100%'}).after(() => { + powerButtonAnimation(this.centerGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + this.pressedShadow.animateRestore().after(() => { if (!this.value) { this.backgroundShape.addClass('tb-shadow'); } @@ -833,7 +874,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { private offLabelShape: Text; private onCircleShape: Circle; private onLabelShape: Text; - private pressedShadow: Gradient; + private pressedShadow: InnerShadowCircle; private pressedTimeline: Timeline; private centerGroup: G; private onCenterGroup: G; @@ -858,7 +899,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { .addTo(this.onCenterGroup); this.onLabelShape = this.createOnLabel('800'); this.createMask(this.onCircleShape, [this.onLabelShape]); - this.pressedShadow = this.createPressedShadow(powerButtonShapeSize - 30); + this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 30, cx, cy, 0, 0); this.backgroundShape.addClass('tb-small-shadow'); this.pressedTimeline = new Timeline(); @@ -895,18 +936,18 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { protected onPressStart() { this.pressedTimeline.finish(); const pressedScale = 0.75; - this.pressedAnimation(this.centerGroup).transform({scale: pressedScale}); - this.pressedAnimation(this.onCenterGroup).transform({scale: 0.98}); - this.pressedAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); - this.pressedAnimation(this.pressedShadow).attr({r: '62%'}); + 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(); - this.pressedAnimation(this.centerGroup).transform({scale: 1}); - this.pressedAnimation(this.onCenterGroup).transform({scale: 1}); - this.pressedAnimation(this.onLabelShape).transform({scale: 1}); - this.pressedAnimation(this.pressedShadow).attr({r: '100%'}); + 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/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== From c506d258890fcc170f7c00e6ff4576a52a6b529e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 12 Feb 2024 13:50:04 +0200 Subject: [PATCH 122/128] Update aggregated-value-card-widget.component.ts --- .../widget/lib/cards/aggregated-value-card-widget.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc3b095552..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'; } - const numeric = formatNumberValue(value, (aggValue.key.decimals || this.ctx.decimals)); aggValue.color.update(value); + const numeric = formatNumberValue(value, (aggValue.key.decimals || this.ctx.decimals)); if (aggValue.showArrow && isDefined(numeric)) { aggValue.upArrow = numeric > 0; aggValue.downArrow = numeric < 0; From a4c5617cc2a849bb74a1337172f65ec3fe1a6e95 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 12 Feb 2024 16:24:02 +0200 Subject: [PATCH 123/128] Improve edge service to find active state based on persistToTelemetry else attribute --- .../device/DeviceActorMessageProcessor.java | 3 +-- .../rpc/processor/BaseEdgeProcessorTest.java | 4 +++ .../server/dao/edge/EdgeService.java | 2 ++ .../server/dao/edge/EdgeServiceImpl.java | 26 ++++++++++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-) 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 9f92838712..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 @@ -213,8 +213,7 @@ 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 { - Optional edgeAttributeOpt = systemContext.getAttributesService().find(tenantId, edgeId, DataConstants.SERVER_SCOPE, DefaultDeviceStateService.ACTIVITY_STATE).get(); - if (edgeAttributeOpt.isPresent() && edgeAttributeOpt.get().getBooleanValue().orElse(false)) { + 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); 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/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/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); From e537479b884191dc67b6ff98ba636708a60406db Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 14 Feb 2024 13:53:44 +0200 Subject: [PATCH 124/128] UI: Implement slider widget. --- .../widget_bundles/control_widgets.json | 1 + .../data/json/system/widget_types/slider.json | 39 ++ .../basic/basic-widget-config.module.ts | 10 +- .../power-button-basic-config.component.html | 6 +- .../single-switch-basic-config.component.html | 4 +- .../rpc/slider-basic-config.component.html | 270 ++++++++++++++ .../rpc/slider-basic-config.component.ts | 309 ++++++++++++++++ .../widget/lib/action/action-widget.models.ts | 11 +- .../widget/lib/action/action-widget.scss | 31 -- .../action-button-widget.component.html | 6 - .../command-button-widget.component.html | 6 - .../rpc/power-button-widget.component.html | 6 - .../rpc/single-switch-widget.component.html | 6 - .../lib/rpc/slider-widget.component.html | 59 +++ .../lib/rpc/slider-widget.component.scss | 135 +++++++ .../widget/lib/rpc/slider-widget.component.ts | 347 ++++++++++++++++++ .../widget/lib/rpc/slider-widget.models.ts | 196 ++++++++++ ...ower-button-widget-settings.component.html | 6 +- .../widget-button-appearance.component.html | 2 +- ...ngle-switch-widget-settings.component.html | 4 +- .../slider-widget-settings.component.html | 215 +++++++++++ .../slider-widget-settings.component.ts | 186 ++++++++++ .../lib/settings/widget-settings.module.ts | 12 +- .../widget/widget-components.module.ts | 7 +- .../app/shared/components/toast.directive.ts | 1 + .../assets/locale/locale.constant-en_US.json | 25 ++ .../assets/widget/slider/default-layout.svg | 48 +++ .../assets/widget/slider/extended-layout.svg | 55 +++ .../widget/slider/simplified-layout.svg | 47 +++ 29 files changed, 1969 insertions(+), 81 deletions(-) create mode 100644 application/src/main/data/json/system/widget_types/slider.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.ts create mode 100644 ui-ngx/src/assets/widget/slider/default-layout.svg create mode 100644 ui-ngx/src/assets/widget/slider/extended-layout.svg create mode 100644 ui-ngx/src/assets/widget/slider/simplified-layout.svg 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 568a144874..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 @@ -11,6 +11,7 @@ "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/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/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index c268be7bcd..32a6a755b4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -103,6 +103,7 @@ import { import { PowerButtonBasicConfigComponent } from '@home/components/widget/config/basic/button/power-button-basic-config.component'; +import { SliderBasicConfigComponent } from '@home/components/widget/config/basic/rpc/slider-basic-config.component'; @NgModule({ declarations: [ @@ -135,7 +136,8 @@ import { SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, CommandButtonBasicConfigComponent, - PowerButtonBasicConfigComponent + PowerButtonBasicConfigComponent, + SliderBasicConfigComponent ], imports: [ CommonModule, @@ -172,7 +174,8 @@ import { SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, CommandButtonBasicConfigComponent, - PowerButtonBasicConfigComponent + PowerButtonBasicConfigComponent, + SliderBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -203,5 +206,6 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type
-
+
{{ 'widgets.power-button.power-on-colors' | translate }}
@@ -134,7 +134,7 @@
-
+
{{ 'widgets.power-button.power-off-colors' | translate }}
@@ -152,7 +152,7 @@
-
+
{{ 'widgets.power-button.disabled-colors' | translate }}
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 18fa3b5587..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 @@ -82,7 +82,7 @@ {{ 'widgets.single-switch.auto-scale' | translate }}
-
+
{{ 'widgets.single-switch.label' | translate }} @@ -99,7 +99,7 @@
-
+
{{ 'widgets.single-switch.icon' | translate }} 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..76c4ff9ea3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/rpc/slider-basic-config.component.html @@ -0,0 +1,270 @@ + + + +
+
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/lib/action/action-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts index dc56a1e992..eb22e2a014 100644 --- 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 @@ -62,8 +62,6 @@ export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, A loading$ = this.loadingSubject.asObservable().pipe(share()); - error = ''; - protected constructor(protected cd: ChangeDetectorRef) { } @@ -99,8 +97,7 @@ export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, A } public clearError() { - this.error = ''; - this.cd.markForCheck(); + this.ctx.hideToast(this.ctx.toastTargetId); } protected createValueGetter(getValueSettings: GetValueSettings, @@ -113,7 +110,8 @@ export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, A } }, error: (err: any) => { - this.onError(err); + const message = parseError(this.ctx, err); + this.onError(message); if (valueObserver?.error) { valueObserver.error(err); } @@ -132,8 +130,7 @@ export abstract class BasicActionWidgetComponent implements OnInit, OnDestroy, A } private onError(error: string) { - this.error = error; - this.cd.markForCheck(); + this.ctx.showErrorToast(error, 'bottom', 'center', this.ctx.toastTargetId, true); } protected updateValue(valueSetter: ValueSetter, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss index 6641c2c32e..081cb00f0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.scss @@ -19,34 +19,3 @@ left: 0; right: 0; } -.tb-action-widget-error-container { - position: absolute; - bottom: 0; - left: 0; - right: 0; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - z-index: 1; - .tb-action-widget-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-action-widget-error-text { - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: 16px; - color: rgba(209, 39, 48, 1); - } - .tb-action-widget-error-clear { - color: rgba(209, 39, 48, 1); - } - } -} 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 index 02e2406859..294ba855fe 100644 --- 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 @@ -27,10 +27,4 @@ [ctx]="ctx" (clicked)="onClick($event)"> -
-
-
- -
-
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 index 26f910fdd7..d48ba2f5e4 100644 --- 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 @@ -27,10 +27,4 @@ (clicked)="onClick($event)"> -
-
-
- -
-
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 index 93e394ac5e..48e4106a97 100644 --- 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 @@ -23,10 +23,4 @@
-
-
-
- -
-
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 c1c05cf880..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 @@ -37,10 +37,4 @@
-
-
-
- -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.html new file mode 100644 index 0000000000..f15892dd52 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.html @@ -0,0 +1,59 @@ + +
+
+
+ +
+
+
+
{{ 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..a60cfd84a0 --- /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 = Math.floor(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 = 45; + 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..78603bc058 --- /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.FUNCTION, + 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/power-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/power-button-widget-settings.component.html index ced9bf9575..b37030c884 100644 --- 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 @@ -84,7 +84,7 @@
widgets.power-button.button
-
+
{{ 'widgets.power-button.power-on-colors' | translate }}
@@ -102,7 +102,7 @@
-
+
{{ 'widgets.power-button.power-off-colors' | translate }}
@@ -120,7 +120,7 @@
-
+
{{ 'widgets.power-button.disabled-colors' | translate }}
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 index b4ce9c8ce2..bdecc9d36b 100644 --- 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 @@ -56,7 +56,7 @@
-
+
{{ 'widgets.button.color-palette' | translate }}
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 9350c2d932..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 @@ -81,7 +81,7 @@ {{ 'widgets.single-switch.auto-scale' | translate }}
-
+
{{ 'widgets.single-switch.label' | translate }} @@ -98,7 +98,7 @@
-
+
{{ 'widgets.single-switch.icon' | translate }} 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..9ef4a603ec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/slider-widget-settings.component.html @@ -0,0 +1,215 @@ + + +
+
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/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index d3fe9dac3e..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 @@ -324,6 +324,9 @@ import { 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: [ @@ -440,7 +443,8 @@ import { SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, CommandButtonWidgetSettingsComponent, - PowerButtonWidgetSettingsComponent + PowerButtonWidgetSettingsComponent, + SliderWidgetSettingsComponent ], imports: [ CommonModule, @@ -562,7 +566,8 @@ import { SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, CommandButtonWidgetSettingsComponent, - PowerButtonWidgetSettingsComponent + PowerButtonWidgetSettingsComponent, + SliderWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -651,5 +656,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type { if (this.snackBarRef) { this.snackBarRef.dismiss(); 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 19981797f8..296c56c020 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6342,6 +6342,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", 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a65396c40c82654ab299c6baf61b1788de030b03 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 14 Feb 2024 16:34:56 +0200 Subject: [PATCH 125/128] UI: Minor improvements --- .../src/main/data/json/system/widget_types/progress_bar.json | 3 +-- .../json/system/widget_types/simple_value_and_chart_card.json | 3 +-- .../basic/cards/progress-bar-basic-config.component.html | 4 ++-- .../cards/progress-bar-widget-settings.component.html | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) 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/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/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
From 4dc3d0ee78fdf073c460553d2ed25af6352d27fd Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 14 Feb 2024 17:03:15 +0200 Subject: [PATCH 126/128] UI: Minor improvement --- .../home/components/widget/lib/rpc/slider-widget.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index a60cfd84a0..74b3dab326 100644 --- 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 @@ -298,7 +298,7 @@ export class SliderWidgetComponent extends } let minAspect = 0.2; - let avgContentHeight = 45; + let avgContentHeight = 35; if (this.showTicks) { minAspect += 0.1; avgContentHeight += 20; From a755d4d8518c64d47123026520a7c5a1000754ac Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 14 Feb 2024 18:14:27 +0200 Subject: [PATCH 127/128] UI: Improve slider widget step calculation. --- .../home/components/widget/lib/rpc/slider-widget.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 74b3dab326..37b5319d44 100644 --- 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 @@ -176,7 +176,7 @@ export class SliderWidgetComponent extends if (this.settings.showTickMarks) { const range = this.settings.tickMax - this.settings.tickMin; - this.sliderStep = Math.floor(range / (this.settings.tickMarksCount - 1)); + this.sliderStep = range / (this.settings.tickMarksCount - 1); } const mainColorInstance = tinycolor(this.settings.mainColor); From 5f70100118197d4ff1c70ce71b858da78d8a545e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 14 Feb 2024 18:42:29 +0200 Subject: [PATCH 128/128] UI: Add value converter type to set value settings. --- .../config/basic/rpc/slider-basic-config.component.html | 3 ++- .../components/widget/lib/action/action-widget.models.ts | 4 ++++ .../components/widget/lib/rpc/slider-widget.models.ts | 2 +- .../action/set-value-action-settings-panel.component.html | 3 ++- .../action/set-value-action-settings-panel.component.ts | 6 ++++++ .../common/action/set-value-action-settings.component.ts | 8 ++++++++ .../control/slider-widget-settings.component.html | 3 ++- .../app/shared/models/action-widget-settings.models.ts | 1 + ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 9 files changed, 27 insertions(+), 4 deletions(-) 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 index 76c4ff9ea3..a8d84d4dc4 100644 --- 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 @@ -23,7 +23,7 @@
widgets.slider.initial-value
widgets.slider.on-value-change
{ constructor(protected settings: ValueToDataSettings) { switch (settings.type) { + case ValueToDataType.VALUE: + break; case ValueToDataType.CONSTANT: this.constantValue = this.settings.constantValue; break; @@ -309,6 +311,8 @@ export class ValueToDataConverter { valueToData(value: V): any { switch (this.settings.type) { + case ValueToDataType.VALUE: + return value; case ValueToDataType.CONSTANT: return this.constantValue; case ValueToDataType.FUNCTION: 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 index 78603bc058..c6fe1e290a 100644 --- 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 @@ -138,7 +138,7 @@ export const sliderWidgetDefaultSettings: SliderWidgetSettings = { key: 'state' }, valueToData: { - type: ValueToDataType.FUNCTION, + type: ValueToDataType.VALUE, constantValue: 0, valueToDataFunction: '/* Convert input integer value to RPC parameters or attribute/time-series value */\nreturn value;' } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html index b1fccafac0..4cf5857612 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component.html @@ -97,7 +97,8 @@
{{ (setValueSettingsFormGroup.get('action').value === setValueAction.EXECUTE_RPC ? 'widgets.value-action.parameters' : 'widgets.value-action.value') | translate }}
- {{ 'widgets.value-action.converter-constant' | 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 }} 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 index d718168b11..819ed2a3fc 100644 --- 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 @@ -32,6 +32,7 @@ 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', @@ -45,6 +46,9 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen @Input() panelTitle: string; + @Input() + valueType = ValueType.BOOLEAN; + @Input() setValueSettings: SetValueSettings; @@ -79,6 +83,8 @@ export class SetValueActionSettingsPanelComponent extends PageComponent implemen functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + ValueType = ValueType; + setValueSettingsFormGroup: UntypedFormGroup; constructor(private fb: UntypedFormBuilder, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts index fce1e23673..7b4c8e0428 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/set-value-action-settings.component.ts @@ -36,6 +36,7 @@ import { isDefinedAndNotNull } from '@core/utils'; import { SetValueActionSettingsPanelComponent } from '@home/components/widget/lib/settings/common/action/set-value-action-settings-panel.component'; +import { ValueType } from '@shared/models/constants'; @Component({ selector: 'tb-set-value-action-settings', @@ -58,6 +59,9 @@ export class SetValueActionSettingsComponent implements OnInit, ControlValueAcce @Input() panelTitle: string; + @Input() + valueType = ValueType.BOOLEAN; + @Input() aliasController: IAliasController; @@ -114,6 +118,7 @@ export class SetValueActionSettingsComponent implements OnInit, ControlValueAcce const ctx: any = { setValueSettings: this.modelValue, panelTitle: this.panelTitle, + valueType: this.valueType, aliasController: this.aliasController, targetDevice: this.targetDevice, widgetType: this.widgetType @@ -137,6 +142,9 @@ export class SetValueActionSettingsComponent implements OnInit, ControlValueAcce private updateDisplayValue() { let value: any; switch (this.modelValue.valueToData.type) { + case ValueToDataType.VALUE: + value = 'value'; + break; case ValueToDataType.CONSTANT: value = this.modelValue.valueToData.constantValue; break; 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 index 9ef4a603ec..0f3c3f11a3 100644 --- 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 @@ -22,7 +22,7 @@
widgets.slider.initial-value
widgets.slider.on-value-change
( ); export enum ValueToDataType { + VALUE = 'VALUE', CONSTANT = 'CONSTANT', FUNCTION = 'FUNCTION', NONE = 'NONE' 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 296c56c020..c71f33fca7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6638,6 +6638,7 @@ "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",