From f9d2549c2c901775edc8b074a352aa6ca478551e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 7 Nov 2024 15:23:35 +0100 Subject: [PATCH 001/103] js-executor: added request body on debug level --- msa/js-executor/api/jsInvokeMessageProcessor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msa/js-executor/api/jsInvokeMessageProcessor.ts b/msa/js-executor/api/jsInvokeMessageProcessor.ts index 98b9c0fe0c..fe18c320bc 100644 --- a/msa/js-executor/api/jsInvokeMessageProcessor.ts +++ b/msa/js-executor/api/jsInvokeMessageProcessor.ts @@ -130,7 +130,7 @@ export class JsInvokeMessageProcessor { processCompileRequest(requestId: string, responseTopic: string, headers: any, compileRequest: JsCompileRequest) { const scriptId = JsInvokeMessageProcessor.getScriptId(compileRequest); - this.logger.debug('[%s] Processing compile request, scriptId: [%s]', requestId, scriptId); + this.logger.debug('[%s] Processing compile request, scriptId: [%s], compileRequest [%s]', requestId, scriptId, compileRequest); if (this.scriptMap.has(scriptId)) { const compileResponse = JsInvokeMessageProcessor.createCompileResponse(scriptId, true); this.logger.debug('[%s] Script was already compiled, scriptId: [%s]', requestId, scriptId); @@ -154,7 +154,7 @@ export class JsInvokeMessageProcessor { processInvokeRequest(requestId: string, responseTopic: string, headers: any, invokeRequest: JsInvokeRequest) { const scriptId = JsInvokeMessageProcessor.getScriptId(invokeRequest); - this.logger.debug('[%s] Processing invoke request, scriptId: [%s]', requestId, scriptId); + this.logger.debug('[%s] Processing invoke request, scriptId: [%s], invokeRequest [%s]', requestId, scriptId, invokeRequest); this.executedScriptsCounter++; if (this.executedScriptsCounter % statFrequency == 0) { const nowMs = performance.now(); @@ -217,7 +217,7 @@ export class JsInvokeMessageProcessor { processReleaseRequest(requestId: string, responseTopic: string, headers: any, releaseRequest: JsReleaseRequest) { const scriptId = JsInvokeMessageProcessor.getScriptId(releaseRequest); - this.logger.debug('[%s] Processing release request, scriptId: [%s]', requestId, scriptId); + this.logger.debug('[%s] Processing release request, scriptId: [%s], releaseRequest [%s]', requestId, scriptId, releaseRequest); if (this.scriptMap.has(scriptId)) { const index = this.scriptIds.indexOf(scriptId); if (index > -1) { From 53da8cd47e3df424484463f057a47b1d316f7599 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 28 Nov 2024 18:55:01 +0200 Subject: [PATCH 002/103] UI: Segmented button --- .../json/system/widget_bundles/buttons.json | 1 + .../widget_bundles/control_widgets.json | 1 + .../system/widget_types/segmented_button.json | 43 +++ .../basic/basic-widget-config.module.ts | 6 + ...gmented-button-basic-config.component.html | 271 ++++++++++++++ ...segmented-button-basic-config.component.ts | 145 ++++++++ .../segmented-button-widget.component.html | 33 ++ .../segmented-button-widget.component.scss | 37 ++ .../segmented-button-widget.component.ts | 118 ++++++ .../button/segmented-button-widget.models.ts | 346 ++++++++++++++++++ ...nted-button-widget-settings.component.html | 263 +++++++++++++ ...mented-button-widget-settings.component.ts | 134 +++++++ ...n-toggle-custom-style-panel.component.html | 92 +++++ ...n-toggle-custom-style-panel.component.scss | 68 ++++ ...ton-toggle-custom-style-panel.component.ts | 210 +++++++++++ ...-button-toggle-custom-style.component.html | 44 +++ ...-button-toggle-custom-style.component.scss | 46 +++ ...et-button-toggle-custom-style.component.ts | 169 +++++++++ .../common/widget-settings-common.module.ts | 10 + .../lib/settings/widget-settings.module.ts | 6 + .../widget/widget-components.module.ts | 3 + .../widget-button-toggle.component.html | 42 +++ .../widget-button-toggle.component.scss | 189 ++++++++++ .../button/widget-button-toggle.component.ts | 234 ++++++++++++ ui-ngx/src/app/shared/shared.module.ts | 3 + .../assets/locale/locale.constant-en_US.json | 27 +- .../segmented-button/rounded-layout.svg | 1 + .../segmented-button/squared-layout.svg | 1 + 28 files changed, 2541 insertions(+), 2 deletions(-) create mode 100644 application/src/main/data/json/system/widget_types/segmented_button.json create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.ts create mode 100644 ui-ngx/src/app/shared/components/button/widget-button-toggle.component.html create mode 100644 ui-ngx/src/app/shared/components/button/widget-button-toggle.component.scss create mode 100644 ui-ngx/src/app/shared/components/button/widget-button-toggle.component.ts create mode 100644 ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg create mode 100644 ui-ngx/src/assets/widget/segmented-button/squared-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 115db85e66..aaed9f73e1 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -11,6 +11,7 @@ "action_button", "command_button", "toggle_button", + "segmented_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 f61219bfe6..6bc4bdc830 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", "toggle_button", + "segmented_button", "power_button", "slider", "control_widgets.switch_control", diff --git a/application/src/main/data/json/system/widget_types/segmented_button.json b/application/src/main/data/json/system/widget_types/segmented_button.json new file mode 100644 index 0000000000..55e1165b83 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/segmented_button.json @@ -0,0 +1,43 @@ +{ + "fqn": "segmented_button", + "name": "Segmented button", + "deprecated": false, + "image": "tb-image;/api/images/system/segmented-button.svg", + "description": "Facilitates 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 stat", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n maxDatasources: 1,\n maxDataKeys: 0,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n hideDataSettings: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-segmented-button-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-segmented-button-basic-config", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#E8E8E8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"initialState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":true,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"},\"executeRpc\":{\"method\":null,\"requestTimeout\":null,\"requestPersistent\":null,\"persistentPollingInterval\":null}},\"leftButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"rightButtonClick\":{\"type\":\"updateDashboardState\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"appearance\":{\"layout\":\"squared\",\"autoScale\":true,\"cardBorder\":1,\"cardBorderColor\":\"#305680\",\"leftAppearance\":{\"showLabel\":true,\"label\":\"Traditional\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"rightAppearance\":{\"showLabel\":true,\"label\":\"Hi-Perf\",\"labelFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":14,\"sizeUnit\":\"px\",\"lineHeight\":\"18px\"},\"showIcon\":true,\"icon\":\"rocket_launch\",\"iconSize\":24,\"iconSizeUnit\":\"px\"},\"selectedStyle\":{\"mainColor\":\"#FFFFFF\",\"backgroundColor\":\"#305680\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}},\"unselectedStyle\":{\"mainColor\":\"#000000C2\",\"backgroundColor\":\"#E8E8E8\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"disabled\":null}}}},\"title\":\"Segmented button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"datasources\":[],\"actions\":{\"click\":[{\"id\":\"56ebc389-f91d-fc25-36ef-0d18329fbeda\",\"name\":\"onClick\",\"icon\":\"more_horiz\",\"type\":\"doNothing\",\"targetDashboardStateId\":null,\"openRightLayout\":false,\"setEntityId\":true,\"stateEntityParamName\":null,\"openInSeparateDialog\":false,\"openInPopover\":false}]},\"borderRadius\":\"4px\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" + }, + "resources": [ + { + "link": "/api/images/system/segmented-button.svg", + "title": "\"Action button\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "segmented-button.svg", + "publicResourceKey": "koboT6A2CqNyRYPTP3ENmjMM6P8KK1PP", + "mediaType": "image/svg+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPHJlY3QgeD0iLjM4IiB5PSI1OC4yIiB3aWR0aD0iMTk5LjI1IiBoZWlnaHQ9IjQzLjYxIiByeD0iMi42MyIgc3Ryb2tlPSIjMzA1NjgwIiBzdHJva2Utd2lkdGg9Ii43NSIvPgogPHJlY3QgeD0iMy4wMSIgeT0iNjAuODMiIHdpZHRoPSI5Ni45OSIgaGVpZ2h0PSIzOC4zNSIgcng9IjEuNSIgZmlsbD0iIzMwNTY4MCIvPgogPHBhdGggZD0iTTI5LjI4IDc1LjUyVjgzSDI4di03LjQ4aDEuMjh6bTIuMzUgMHYxLjAyaC01Ljk2di0xLjAyaDUuOTZ6bTEuOTIgMi45OFY4M2gtMS4yM3YtNS41NmgxLjE4bC4wNSAxLjA2em0xLjctMS4xdjEuMTVhMi40NCAyLjQ0IDAgMCAwLTEuMDcuMDYgMS4wNiAxLjA2IDAgMCAwLS42NS42NWMtLjA2LjE2LS4xLjM0LS4xLjUzbC0uMjkuMDJjMC0uMzUuMDQtLjY3LjEtLjk3LjA3LS4zLjE4LS41Ni4zMS0uNzguMTQtLjIzLjMyLS40LjUzLS41M2ExLjQgMS40IDAgMCAxIC45OC0uMTdsLjIuMDR6bTMuOTIgNC40OHYtMi42NWMwLS4yLS4wNC0uMzctLjEtLjUxYS43Ni43NiAwIDAgMC0uMzQtLjM0IDEuMTIgMS4xMiAwIDAgMC0uNTQtLjExYy0uMiAwLS4zOC4wMy0uNTMuMWEuODUuODUgMCAwIDAtLjM0LjI4LjY3LjY3IDAgMCAwLS4xMi40aC0xLjI0YzAtLjIzLjA2LS40NC4xNi0uNjUuMS0uMi4yNi0uMzguNDYtLjU1LjItLjE2LjQ1LS4yOC43My0uMzguMjgtLjA5LjYtLjEzLjk0LS4xMy40MiAwIC44LjA3IDEuMTIuMi4zMi4xNS41OC4zNi43Ni42NC4xOS4yOC4yOC42NC4yOCAxLjA2djIuNDdjMCAuMjYuMDIuNDkuMDYuNjkuMDMuMi4wOS4zNy4xNS41MlY4M2gtMS4yN2EyLjIgMi4yIDAgMCAxLS4xMy0uNWMtLjAzLS4yMi0uMDUtLjQyLS4wNS0uNjJ6bS4xOC0yLjI2LjAxLjc2aC0uODljLS4yMyAwLS40My4wMy0uNi4wNy0uMTguMDQtLjMzLjEtLjQ0LjE5cy0uMi4xOC0uMjYuM2EuODcuODcgMCAwIDAtLjEuMzljMCAuMTUuMDQuMjguMTEuNC4wNy4xMy4xNy4yMi4zLjI5LjEzLjA3LjMuMS40OC4xYTEuMzYgMS4zNiAwIDAgMCAxLjEyLS41NGMuMS0uMTUuMTctLjMuMTctLjQ0bC40LjU1Yy0uMDQuMTQtLjEuMy0uMi40Ni0uMS4xNi0uMjQuMzEtLjQuNDZhMS45NCAxLjk0IDAgMCAxLTEuMzMuNWMtLjM2IDAtLjY5LS4wOC0uOTctLjIyLS4yOS0uMTUtLjUtLjM1LS42Ny0uNmExLjc3IDEuNzcgMCAwIDEtLjA4LTEuNjNjLjEyLS4yMi4yOC0uNDIuNS0uNTcuMjItLjE1LjQ4LS4yNy44LS4zNS4zMS0uMDguNjctLjEyIDEuMDgtLjEyaC45N3ptNS45NCAyLjIzVjc1LjFoMS4yNFY4M2gtMS4xMmwtLjEyLTEuMTV6bS0zLjYyLTEuNTd2LS4xYzAtLjQzLjA1LS44MS4xNS0xLjE2LjEtLjM1LjI0LS42NS40My0uOWExLjkgMS45IDAgMCAxIDEuNi0uNzggMS44NCAxLjg0IDAgMCAxIDEuNTMuNzZjLjE4LjIzLjMzLjUyLjQzLjg1LjExLjM0LjE4LjcuMjMgMS4xMXYuMzVjLS4wNS40LS4xMi43Ni0uMjMgMS4wOS0uMS4zMy0uMjUuNjEtLjQyLjg1YTEuODQgMS44NCAwIDAgMS0xLjU1Ljc1IDEuOTUgMS45NSAwIDAgMS0xLjU5LS44IDIuNzkgMi43OSAwIDAgMS0uNDMtLjljLS4xLS4zNC0uMTUtLjcyLS4xNS0xLjEyem0xLjI0LS4xdi4xYzAgLjI1LjAyLjUuMDcuNzEuMDQuMjIuMTIuNDEuMjIuNTguMS4xNy4yMi4zLjM4LjQuMTYuMDguMzYuMTMuNTguMTMuMjggMCAuNTEtLjA2LjctLjE4cy4zMi0uMy40Mi0uNWMuMS0uMjIuMTgtLjQ1LjIxLS43MXYtLjkzYy0uMDItLjItLjA2LS40LS4xMi0uNTdhMS41MSAxLjUxIDAgMCAwLS4yNi0uNDYgMS4yNCAxLjI0IDAgMCAwLS45NS0uNDJjLS4yMiAwLS40MS4wNS0uNTcuMTUtLjE1LjEtLjI4LjIyLS4zOS40LS4xLjE2LS4xNy4zNi0uMjIuNThzLS4wNy40Ni0uMDcuNzF6bTYuNDMtMi43NFY4M0g0OC4xdi01LjU2aDEuMjR6bS0xLjMyLTEuNDZjMC0uMTkuMDYtLjM1LjE4LS40N2EuNy43IDAgMCAxIC41My0uMTljLjIxIDAgLjM5LjA2LjUxLjIuMTMuMTEuMi4yNy4yLjQ2IDAgLjE4LS4wNy4zNC0uMi40NmEuNzEuNzEgMCAwIDEtLjUxLjE5LjcyLjcyIDAgMCAxLS41My0uMTkuNjMuNjMgMCAwIDEtLjE4LS40NnptNS40MiAxLjQ2di45SDUwLjN2LS45aDMuMTR6bS0yLjIzLTEuMzZoMS4yM3Y1LjM4YzAgLjE3LjAzLjMuMDguNC4wNS4wOS4xMi4xNS4yLjE4LjEuMDMuMi4wNC4zMi4wNGExLjkgMS45IDAgMCAwIC40NC0uMDR2Ljk0YTMuMTcgMy4xNyAwIDAgMS0uODIuMTJjLS4yOCAwLS41NC0uMDUtLjc2LS4xNS0uMjEtLjEtLjM5LS4yNy0uNS0uNWExLjk0IDEuOTQgMCAwIDEtLjItLjkxdi01LjQ2em00LjcgMS4zNlY4M2gtMS4yNXYtNS41NmgxLjI1em0tMS4zMy0xLjQ2YzAtLjE5LjA2LS4zNS4xOS0uNDdhLjcuNyAwIDAgMSAuNTItLjE5Yy4yMiAwIC40LjA2LjUyLjIuMTMuMTEuMTkuMjcuMTkuNDYgMCAuMTgtLjA2LjM0LS4yLjQ2YS43MS43MSAwIDAgMS0uNTEuMTkuNzIuNzIgMCAwIDEtLjUyLS4xOS42My42MyAwIDAgMS0uMTktLjQ2em0yLjYzIDQuM3YtLjEyYzAtLjQuMDYtLjc3LjE4LTEuMTEuMTItLjM1LjI4LS42NS41LS45LjIzLS4yNi41LS40Ni44Mi0uNi4zMi0uMTQuNjgtLjIxIDEuMDktLjIxLjQgMCAuNzcuMDcgMS4wOC4yMS4zMy4xNC42LjM0LjgyLjYuMjIuMjUuNC41NS41MS45LjEyLjM0LjE4LjcxLjE4IDEuMTF2LjEyYzAgLjQtLjA2Ljc3LS4xOCAxLjEyYTIuMzYgMi4zNiAwIDAgMS0yLjQgMS43Yy0uNDEgMC0uNzgtLjA3LTEuMS0uMmEyLjM2IDIuMzYgMCAwIDEtLjgxLS42Yy0uMjItLjI2LS40LS41Ni0uNTEtLjlhMy40NCAzLjQ0IDAgMCAxLS4xOC0xLjEyem0xLjI0LS4xMnYuMTJjMCAuMjUuMDMuNDkuMDguNzEuMDUuMjIuMTMuNDIuMjQuNTkuMTEuMTYuMjUuMy40Mi40LjE4LjA5LjM4LjE0LjYyLjE0YTEuMTggMS4xOCAwIDAgMCAxLjAxLS41NGMuMTEtLjE3LjItLjM3LjI0LS41OS4wNi0uMjIuMDktLjQ2LjA5LS43di0uMTNjMC0uMjQtLjAzLS40OC0uMDktLjdhMS44IDEuOCAwIDAgMC0uMjQtLjU5IDEuMTggMS4xOCAwIDAgMC0xLjAyLS41NWMtLjI0IDAtLjQ0LjA1LS42MS4xNS0uMTcuMS0uMy4yMy0uNDIuNC0uMS4xNy0uMTkuMzctLjI0LjYtLjA1LjIxLS4wOC40NS0uMDguN3ptNi4zOS0xLjUzVjgzSDYzLjZ2LTUuNTZoMS4xN2wuMDcgMS4xOXpNNjQuNjIgODBoLS40YzAtLjQuMDYtLjc2LjE2LTEuMDkuMTEtLjMzLjI2LS42LjQ2LS44NGEyIDIgMCAwIDEgMS42LS43NGMuMjcgMCAuNS4wMy43Mi4xLjIyLjA4LjQuMi41Ni4zNi4xNi4xNy4yOC4zOC4zNi42NS4wOS4yNi4xMy41OC4xMy45NlY4M2gtMS4yNXYtMy42YzAtLjI3LS4wNC0uNDgtLjEyLS42M2EuNjYuNjYgMCAwIDAtLjMzLS4zM2MtLjE0LS4wNy0uMzItLjEtLjU0LS4xYTEuMiAxLjIgMCAwIDAtMSAuNWMtLjEuMTUtLjIuMzItLjI2LjUzLS4wNi4yLS4xLjQxLS4xLjY0em04LjE3IDEuODd2LTIuNjVjMC0uMi0uMDMtLjM3LS4xLS41MWEuNzYuNzYgMCAwIDAtLjMzLS4zNCAxLjEyIDEuMTIgMCAwIDAtLjU1LS4xMWMtLjIgMC0uMzcuMDMtLjUyLjFhLjg1Ljg1IDAgMCAwLS4zNS4yOC42Ny42NyAwIDAgMC0uMTIuNEg2OS42YzAtLjIzLjA1LS40NC4xNi0uNjUuMS0uMi4yNi0uMzguNDYtLjU1LjItLjE2LjQ0LS4yOC43Mi0uMzguMjgtLjA5LjYtLjEzLjk1LS4xMy40MiAwIC43OS4wNyAxLjExLjIuMzMuMTUuNTguMzYuNzcuNjQuMTguMjguMjguNjQuMjggMS4wNnYyLjQ3YzAgLjI2LjAyLjQ5LjA1LjY5LjA0LjIuMDkuMzcuMTYuNTJWODNoLTEuMjdhMi4yIDIuMiAwIDAgMS0uMTQtLjVjLS4wMy0uMjItLjA1LS40Mi0uMDUtLjYyem0uMTgtMi4yNi4wMS43NmgtLjg4Yy0uMjMgMC0uNDQuMDMtLjYxLjA3LS4xOC4wNC0uMzIuMS0uNDQuMTlzLS4yLjE4LS4yNi4zYS44Ny44NyAwIDAgMC0uMDkuMzljMCAuMTUuMDQuMjguMS40LjA3LjEzLjE3LjIyLjMuMjkuMTQuMDcuMy4xLjQ5LjFhMS4zNiAxLjM2IDAgMCAwIDEuMTEtLjU0Yy4xMS0uMTUuMTctLjMuMTgtLjQ0bC40LjU1YTIuMiAyLjIgMCAwIDEtLjYuOTEgMS45NCAxLjk0IDAgMCAxLTEuMzQuNWMtLjM2IDAtLjY4LS4wNy0uOTctLjIxLS4yOC0uMTUtLjUtLjM1LS42Ni0uNmExLjc3IDEuNzcgMCAwIDEtLjA4LTEuNjNjLjExLS4yMi4yOC0uNDIuNS0uNTcuMjEtLjE1LjQ4LS4yNy44LS4zNS4zLS4wOC42Ny0uMTIgMS4wNy0uMTJoLjk3em0zLjg4LTQuNTJWODNINzUuNnYtNy45aDEuMjV6IiBmaWxsPSIjZmZmIi8+CiA8cGF0aCBkPSJNMTM2Ljg2IDc4LjY0djEuMDJoLTMuOTd2LTEuMDJoMy45N3ptLTMuNjUtMy4xMlY4M2gtMS4zdi03LjQ4aDEuM3ptNC42NCAwVjgzaC0xLjI4di03LjQ4aDEuMjh6bTIuOTMgMS45MlY4M2gtMS4yNHYtNS41NmgxLjI0em0tMS4zMy0xLjQ2YzAtLjE5LjA3LS4zNS4xOS0uNDcuMTMtLjEzLjMtLjE5LjUyLS4xOS4yMiAwIC40LjA2LjUyLjIuMTMuMTEuMi4yNy4yLjQ2IDAgLjE4LS4wNy4zNC0uMi40NmEuNzEuNzEgMCAwIDEtLjUyLjE5LjcyLjcyIDAgMCAxLS41Mi0uMTkuNjMuNjMgMCAwIDEtLjE5LS40NnptNS4zNyAzLjMzdi45OWgtMi43MnYtMWgyLjcyem00LjI0LjloLTEuOTV2LTEuMDJoMS45NGMuMzQgMCAuNjItLjA2LjgzLS4xNy4yLS4xLjM2LS4yNi40Ni0uNDUuMS0uMi4xNC0uNDIuMTQtLjY3IDAtLjI0LS4wNS0uNDYtLjE0LS42Ni0uMS0uMjEtLjI1LS4zOC0uNDYtLjVzLS40OS0uMi0uODItLjJoLTEuNTZWODNoLTEuMjl2LTcuNDhoMi44NWMuNTcgMCAxLjA3LjEgMS40Ny4zLjQuMi43Mi40OS45My44NS4yMS4zNS4zMi43Ni4zMiAxLjIyIDAgLjQ4LS4xLjktLjMyIDEuMjQtLjIxLjM1LS41Mi42Mi0uOTMuOC0uNC4xOS0uOS4yOC0xLjQ3LjI4em02LjMyIDIuOWMtLjQxIDAtLjc4LS4wNy0xLjExLS4yYTIuNDYgMi40NiAwIDAgMS0xLjM4LTEuNDQgMy4wMSAzLjAxIDAgMCAxLS4xOC0xLjA2di0uMmMwLS40NC4wNi0uODQuMTktMS4xOS4xMi0uMzUuMy0uNjUuNTMtLjkuMjItLjI2LjQ5LS40NS44LS41OC4zLS4xNC42NC0uMiAxLS4yLjQgMCAuNzUuMDYgMS4wNC4yLjMuMTMuNTUuMzIuNzQuNTYuMi4yNC4zNS41My40NS44Ni4xLjMzLjE1LjcuMTUgMS4xdi41M2gtNC4zdi0uODloMy4wN3YtLjFjMC0uMjItLjA1LS40My0uMTMtLjYyLS4wOC0uMi0uMi0uMzYtLjM3LS40OGExLjEgMS4xIDAgMCAwLS42NS0uMThjLS4yMSAwLS40LjA1LS41NS4xNC0uMTYuMDgtLjMuMi0uNC4zN3MtLjE5LjM2LS4yNS42YTMuNCAzLjQgMCAwIDAtLjA4Ljc3di4yYzAgLjI1LjAzLjQ4LjEuNjkuMDcuMi4xNy4zOC4zLjU0YTEuMzQgMS4zNCAwIDAgMCAxLjEuNWMuMyAwIC41Ny0uMDcuOC0uMTlzLjQzLS4yOS42LS41bC42Ni42MmEyLjM0IDIuMzQgMCAwIDEtMS4xNy44OWMtLjI4LjEtLjYuMTUtLjk2LjE1ek0xNjAgNzguNVY4M2gtMS4yNHYtNS41NmgxLjE5bC4wNSAxLjA2em0xLjctMS4xdjEuMTVhMi40NyAyLjQ3IDAgMCAwLTEuMDcuMDYgMS4wNSAxLjA1IDAgMCAwLS42NS42NWMtLjA2LjE2LS4xLjM0LS4xLjUzbC0uMjkuMDJjMC0uMzUuMDQtLjY3LjEtLjk3LjA3LS4zLjE4LS41Ni4zMS0uNzguMTQtLjIzLjMyLS40LjUzLS41M2ExLjQgMS40IDAgMCAxIC45Ny0uMTdsLjIuMDR6bTIuNzQgNS42aC0xLjI0di02LjFjMC0uNDEuMDgtLjc2LjIzLTEuMDQuMTYtLjI4LjM4LS41LjY3LS42NGEyLjU4IDIuNTggMCAwIDEgMS43NS0uMTNsLS4wMy45NmExLjA0IDEuMDQgMCAwIDAtLjk3LjA2LjcyLjcyIDAgMCAwLS4zMS4zYy0uMDcuMTMtLjEuMy0uMS41Vjgzem0xLjE0LTUuNTZ2LjloLTMuMjR2LS45aDMuMjR6IiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii43NiIvPgo8L3N2Zz4K", + "public": true + } + ], + "scada": false, + "tags": [ + "button", + "action", + "navigation", + "navigate", + "dashboard state" + ] +} \ 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 a32d7c2311..800deec7e5 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 @@ -143,6 +143,9 @@ import { UnreadNotificationBasicConfigComponent } from '@home/components/widget/config/basic/cards/unread-notification-basic-config.component'; import { ScadaSymbolBasicConfigComponent } from '@home/components/widget/config/basic/scada/scada-symbol-basic-config.component'; +import { + SegmentedButtonBasicConfigComponent +} from '@home/components/widget/config/basic/button/segmented-button-basic-config.component'; @NgModule({ declarations: [ @@ -174,6 +177,7 @@ import { ScadaSymbolBasicConfigComponent } from '@home/components/widget/config/ BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, + SegmentedButtonBasicConfigComponent, CommandButtonBasicConfigComponent, PowerButtonBasicConfigComponent, SliderBasicConfigComponent, @@ -227,6 +231,7 @@ import { ScadaSymbolBasicConfigComponent } from '@home/components/widget/config/ BarChartWithLabelsBasicConfigComponent, SingleSwitchBasicConfigComponent, ActionButtonBasicConfigComponent, + SegmentedButtonBasicConfigComponent, CommandButtonBasicConfigComponent, PowerButtonBasicConfigComponent, SliderBasicConfigComponent, @@ -272,6 +277,7 @@ export const basicWidgetConfigComponentsMap: {[key: string]: Type + + + +
+
widgets.action-button.behavior
+
+
widgets.button-state.initial-state
+ +
+ +
+
widgets.action-button.left-button-click
+ + +
+
+
widgets.action-button.right-button-click
+ + +
+ +
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + + + {{ segmentedButtonLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.button.auto-scale' | translate }} + +
+
+
widgets.segmented-button.card-border
+
+ + +
px
+
+ + +
+
+
+ +
+
+
widgets.segmented-button.button-appearance
+ + {{ 'widgets.segmented-button.left' | translate }} + {{ 'widgets.segmented-button.right' | translate }} + +
+
+
+ + {{ 'widgets.button.label' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.button.icon' | translate }} + +
+ + + + + + +
+
+
+ +
+
+ + {{ 'widgets.button.label' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.button.icon' | translate }} + +
+ + + + + + +
+
+
+
+ +
+
+
widgets.segmented-button.color-styles
+ + {{ 'widgets.segmented-button.selected' | translate }} + {{ 'widgets.segmented-button.unselected' | translate }} + +
+
+
+
{{ 'widgets.button.color-palette' | translate }}
+
+
+
widgets.button.main
+ + +
+ +
+
widgets.button.background
+ + +
+
+
+
+ + + +
widgets.button.custom-styles
+
+
+ +
+
{{ widgetButtonToggleStatesTranslationsMap.get(state) | translate }}
+ + +
+
+
+
+
+
+
+
{{ 'widgets.button.color-palette' | translate }}
+
+
+
widgets.button.main
+ + +
+ +
+
widgets.button.background
+ + +
+
+
+
+ + + +
widgets.button.custom-styles
+
+
+ +
+
{{ widgetButtonToggleStatesTranslationsMap.get(state) | translate }}
+ + +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts new file mode 100644 index 0000000000..2b86d88a4a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts @@ -0,0 +1,145 @@ +/// +/// 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 { Datasource, TargetDevice, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { ValueType } from '@shared/models/constants'; +import { getTargetDeviceFromDatasources } from '@shared/models/widget-settings.models'; +import { + SegmentedButtonAppearanceType, + SegmentedButtonColorStylesType, + segmentedButtonDefaultSettings, + segmentedButtonLayoutBorder, + segmentedButtonLayoutImages, + segmentedButtonLayouts, + segmentedButtonLayoutTranslations, + SegmentedButtonWidgetSettings, + WidgetButtonToggleState, + widgetButtonToggleStatesTranslations +} from '@home/components/widget/lib/button/segmented-button-widget.models'; + +@Component({ + selector: 'tb-segmented-button-basic-config', + templateUrl: './segmented-button-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class SegmentedButtonBasicConfigComponent extends BasicWidgetConfigComponent { + + get targetDevice(): TargetDevice { + const datasources: Datasource[] = this.segmentedButtonWidgetConfigForm.get('datasources').value; + return getTargetDeviceFromDatasources(datasources); + } + + segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'left'; + segmentedButtonColorStylesType: SegmentedButtonColorStylesType = 'selected'; + + widgetButtonToggleStates = Object.keys(WidgetButtonToggleState) as WidgetButtonToggleState[]; + widgetButtonToggleStatesTranslationsMap = widgetButtonToggleStatesTranslations; + + segmentedButtonLayouts = segmentedButtonLayouts; + segmentedButtonLayoutTranslationMap = segmentedButtonLayoutTranslations; + segmentedButtonLayoutImageMap = segmentedButtonLayoutImages; + segmentedButtonLayoutBorderMap = segmentedButtonLayoutBorder; + + valueType = ValueType; + + segmentedButtonWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.segmentedButtonWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: SegmentedButtonWidgetSettings = {...segmentedButtonDefaultSettings, ...(configData.config.settings || {})}; + + this.segmentedButtonWidgetConfigForm = this.fb.group({ + datasources: [configData.config.datasources, []], + + initialState: [settings.initialState, []], + leftButtonClick: [settings.leftButtonClick, []], + rightButtonClick: [settings.rightButtonClick, []], + disabledState: [settings.disabledState, []], + + appearance: this.fb.group({ + layout: [settings.appearance.layout, []], + autoScale: [settings.appearance.autoScale, []], + cardBorder: [settings.appearance.cardBorder, []], + cardBorderColor: [settings.appearance.cardBorderColor, []], + leftAppearance: this.fb.group({ + showLabel: [settings.appearance.leftAppearance.showLabel, []], + label: [settings.appearance.leftAppearance.label, []], + labelFont: [settings.appearance.leftAppearance.labelFont, []], + showIcon: [settings.appearance.leftAppearance.showIcon, []], + icon: [settings.appearance.leftAppearance.icon, []], + iconSize: [settings.appearance.leftAppearance.iconSize, []], + iconSizeUnit: [settings.appearance.leftAppearance.iconSizeUnit, []], + }), + rightAppearance: this.fb.group({ + showLabel: [settings.appearance.rightAppearance.showLabel, []], + label: [settings.appearance.rightAppearance.label, []], + labelFont: [settings.appearance.rightAppearance.labelFont, []], + showIcon: [settings.appearance.rightAppearance.showIcon, []], + icon: [settings.appearance.rightAppearance.icon, []], + iconSize: [settings.appearance.rightAppearance.iconSize, []], + iconSizeUnit: [settings.appearance.rightAppearance.iconSizeUnit, []], + }), + selectedStyle: this.fb.group({ + mainColor: [settings.appearance.selectedStyle.mainColor, []], + backgroundColor: [settings.appearance.selectedStyle.backgroundColor, []], + customStyle: this.fb.group({ + enabled: [settings.appearance.selectedStyle.customStyle.enabled, []], + hovered: [settings.appearance.selectedStyle.customStyle.hovered, []], + disabled: [settings.appearance.selectedStyle.customStyle.disabled, []], + }) + }), + unselectedStyle: this.fb.group({ + mainColor: [settings.appearance.unselectedStyle.mainColor, []], + backgroundColor: [settings.appearance.unselectedStyle.backgroundColor, []], + customStyle: this.fb.group({ + enabled: [settings.appearance.unselectedStyle.customStyle.enabled, []], + hovered: [settings.appearance.unselectedStyle.customStyle.hovered, []], + disabled: [settings.appearance.unselectedStyle.customStyle.disabled, []], + }) + }), + }) + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + this.widgetConfig.config.datasources = config.datasources; + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.initialState = config.initialState; + this.widgetConfig.config.settings.disabledState = config.disabledState; + this.widgetConfig.config.settings.leftButtonClick = config.leftButtonClick; + this.widgetConfig.config.settings.rightButtonClick = config.rightButtonClick; + this.widgetConfig.config.settings.appearance = config.appearance; + this.widgetConfig.config.borderRadius = this.segmentedButtonLayoutBorderMap.get(config.appearance.layout); + return this.widgetConfig; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html new file mode 100644 index 0000000000..10cf016e11 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html @@ -0,0 +1,33 @@ + +
+ +
+ + +
+ +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss new file mode 100644 index 0000000000..66aec79aa4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss @@ -0,0 +1,37 @@ +/** + * 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-segmented-button-widget { + width: 100%; + height: 100%; + position: relative; + + .tb-segmented-button-container { + height: 100%; + + .tb-widget-button-toggle { + display: block; + height: 100%; + } + } + + > div.tb-segmented-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/segmented-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts new file mode 100644 index 0000000000..6c7ee3eef8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts @@ -0,0 +1,118 @@ +/// +/// 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 { + ButtonToggleAppearance, + segmentedButtonDefaultSettings, + SegmentedButtonWidgetSettings +} from '@home/components/widget/lib/button/segmented-button-widget.models'; +import { ComponentStyle } from '@shared/models/widget-settings.models'; +import { MatButtonToggleChange } from '@angular/material/button-toggle'; + +@Component({ + selector: 'tb-segmented-button-widget', + templateUrl: './segmented-button-widget.component.html', + styleUrls: ['../action/action-widget.scss', './segmented-button-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SegmentedButtonWidgetComponent extends + BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + settings: SegmentedButtonWidgetSettings; + + overlayStyle: ComponentStyle = {}; + + value = false; + disabled = false; + + autoScale: boolean; + appearance: ButtonToggleAppearance; + + constructor(protected imagePipe: ImagePipe, + protected sanitizer: DomSanitizer, + protected cd: ChangeDetectorRef) { + super(cd); + } + + ngOnInit(): void { + super.ngOnInit(); + this.settings = {...segmentedButtonDefaultSettings, ...this.ctx.settings}; + + this.autoScale = this.settings.appearance.autoScale; + + 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.button-state.disabled-state')}; + this.createValueGetter(disabledStateSettings, ValueType.BOOLEAN, { + next: (value) => this.onDisabled(value) + }); + + this.appearance = this.settings.appearance; + } + + ngAfterViewInit(): void { + super.ngAfterViewInit(); + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + public onInit() { + super.onInit(); + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + private onValue(value: boolean): void { + const newValue = !!value; + if (this.value !== newValue) { + this.value = newValue; + this.appearance = this.settings.appearance; + this.cd.markForCheck(); + } + } + + private onDisabled(value: boolean): void { + const newDisabled = !!value; + if (this.disabled !== newDisabled) { + this.disabled = newDisabled; + this.cd.markForCheck(); + } + } + + public onClick(_$event: MatButtonToggleChange) { + if (!this.ctx.isEdit && !this.ctx.isPreview) { + if (_$event.value) { + this.ctx.actionsApi.onWidgetAction(event, this.settings.leftButtonClick); + } else { + this.ctx.actionsApi.onWidgetAction(event, this.settings.rightButtonClick); + } + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts new file mode 100644 index 0000000000..611812f244 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts @@ -0,0 +1,346 @@ +/// +/// 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 } from '@shared/models/action-widget-settings.models'; +import { cssUnit, Font } from '@shared/models/widget-settings.models'; +import { defaultWidgetAction, WidgetAction } from '@shared/models/widget.models'; + +const defaultMainColor = '#305680'; +const defaultBackgroundColor = '#E8E8E8'; + +export const defaultMainColorDisabled = 'rgba(0, 0, 0, 0.38)'; +export const defaultBackgroundColorDisabled = 'rgba(0, 0, 0, 0.03)'; +export const defaultBorderColorDisabled = defaultBackgroundColorDisabled; + +const defaultBorderColor = defaultBackgroundColor; + +export enum SegmentedButtonLayout { + squared = 'squared', + rounded = 'rounded' +} + +export type SegmentedButtonAppearanceType = 'left' | 'right'; +export type SegmentedButtonColorStylesType = 'selected' | 'unselected'; + +export const segmentedButtonLayouts = Object.keys(SegmentedButtonLayout) as SegmentedButtonLayout[]; + +export const segmentedButtonLayoutTranslations = new Map( + [ + [SegmentedButtonLayout.squared, 'widgets.segmented-button.layout-squared'], + [SegmentedButtonLayout.rounded, 'widgets.segmented-button.layout-rounded'] + ] +); + +export const segmentedButtonLayoutImages = new Map( + [ + [SegmentedButtonLayout.squared, 'assets/widget/segmented-button/squared-layout.svg'], + [SegmentedButtonLayout.rounded, 'assets/widget/segmented-button/rounded-layout.svg'] + ] +); + +export const segmentedButtonLayoutBorder = new Map( + [ + [SegmentedButtonLayout.squared, '4px'], + [SegmentedButtonLayout.rounded, '40px'] + ] +); + +export interface SegmentedButtonAppearance { + showLabel: boolean; + label: string; + labelFont: Font; + showIcon: boolean; + icon: string; + iconSize: number; + iconSizeUnit: cssUnit; +} + +export interface SegmentedButtonStyles { + mainColor: string; + backgroundColor: string; + customStyle: WidgetButtonToggleCustomStyles; +} + +export interface ButtonToggleAppearance { + layout: SegmentedButtonLayout; + autoScale: boolean; + cardBorder: number; + cardBorderColor: string; + leftAppearance: SegmentedButtonAppearance; + rightAppearance: SegmentedButtonAppearance; + selectedStyle: SegmentedButtonStyles; + unselectedStyle: SegmentedButtonStyles; +} + +export interface SegmentedButtonWidgetSettings { + initialState: GetValueSettings; + disabledState: GetValueSettings; + leftButtonClick: WidgetAction; + rightButtonClick: WidgetAction; + + appearance: ButtonToggleAppearance; +} + +export const segmentedButtonDefaultAppearance: ButtonToggleAppearance = { + layout: SegmentedButtonLayout.squared, + autoScale: true, + cardBorder: 1, + cardBorderColor: '#305680', + leftAppearance: { + showLabel: true, + label: 'Traditional', + labelFont: { + family: 'Roboto', + weight: '500', + style: 'normal', + size: 14, + sizeUnit: 'px', + lineHeight: '18px' + }, + showIcon: true, + icon: 'home', + iconSize: 24, + iconSizeUnit: 'px', + }, + rightAppearance: { + showLabel: true, + label: 'Hi-Perf', + labelFont: { + family: 'Roboto', + weight: '500', + style: 'normal', + size: 14, + sizeUnit: 'px', + lineHeight: '18px' + }, + showIcon: true, + icon: 'home', + iconSize: 24, + iconSizeUnit: 'px', + }, + selectedStyle: { + mainColor: '#FFFFFF', + backgroundColor: '#00695C', + customStyle: { + enabled: null, + hovered: null, + disabled: null + } + }, + unselectedStyle: { + mainColor: '#000000C2', + backgroundColor: '#E8E8E8', + customStyle: { + enabled: null, + hovered: null, + disabled: null + } + } +} + +export const segmentedButtonDefaultSettings: SegmentedButtonWidgetSettings = { + initialState: { + action: GetValueAction.DO_NOTHING, + defaultValue: true, + getAttribute: { + key: 'state', + scope: null + }, + getTimeSeries: { + key: 'state' + }, + getAlarmStatus: { + severityList: null, + typeList: null + }, + 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' + }, + getAlarmStatus: { + severityList: null, + typeList: null + }, + dataToValue: { + type: DataToValueType.NONE, + compareToValue: true, + dataToValueFunction: '/* Should return boolean value */\nreturn data;' + } + }, + leftButtonClick: defaultWidgetAction(), + rightButtonClick: defaultWidgetAction(), + appearance: segmentedButtonDefaultAppearance +}; + + +const mainCheckedColorVarPrefix = '--tb-widget-button-toggle-main-checked-color-'; +const backgroundCheckedColorVarPrefix = '--tb-widget-button-toggle-background-checked-color-'; +const borderCheckedColorVarPrefix = '--tb-widget-button-toggle-border-checked-color-'; + +const mainUncheckedColorVarPrefix = '--tb-widget-button-toggle-main-unchecked-color-'; +const backgroundUncheckedColorVarPrefix = '--tb-widget-button-toggle-background-unchecked-color-'; +const borderUncheckedColorVarPrefix = '--tb-widget-button-toggle-border-unchecked-color-'; + +export enum WidgetButtonToggleState { + enabled = 'enabled', + hovered = 'hovered', + disabled = 'disabled' +} + +export const widgetButtonToggleStates = Object.keys(WidgetButtonToggleState) as WidgetButtonToggleState[]; + +export const widgetButtonToggleStatesTranslations = new Map( + [ + [WidgetButtonToggleState.enabled, 'widgets.button-state.enabled'], + [WidgetButtonToggleState.hovered, 'widgets.button-state.hovered'], + [WidgetButtonToggleState.disabled, 'widgets.button-state.disabled'] + ] +); + +export interface WidgetButtonToggleCustomStyle { + overrideMainColor?: boolean; + mainColor?: string; + overrideBackgroundColor?: boolean; + backgroundColor?: string; + overrideBorderColor?: boolean; + borderColor?: string; +} + +export type WidgetButtonToggleCustomStyles = Record; + +export abstract class ButtonToggleStateCssGenerator { + + constructor() {} + + public generateStateCss(selectedAppearance: SegmentedButtonStyles, unselectedAppearance: SegmentedButtonStyles): string { + const selectedColor = this.getColors(selectedAppearance); + const unselectedColor = this.getColors(unselectedAppearance); + + return `${mainCheckedColorVarPrefix}${this.state}: ${selectedColor.mainColor};\n`+ + `${backgroundCheckedColorVarPrefix}${this.state}: ${selectedColor.backgroundColor};\n`+ + `${borderCheckedColorVarPrefix}${this.state}: ${selectedColor.borderColor};\n`+ + `${mainUncheckedColorVarPrefix}${this.state}: ${unselectedColor.mainColor};\n`+ + `${backgroundUncheckedColorVarPrefix}${this.state}: ${unselectedColor.backgroundColor};\n`+ + `${borderUncheckedColorVarPrefix}${this.state}: ${unselectedColor.borderColor};`; + } + + private getColors(appearance: SegmentedButtonStyles) { + let mainColor = this.getMainColor(appearance); + let backgroundColor = this.getBackgroundColor(appearance); + let borderColor = this.getBorderColor(); + const stateCustomStyle = appearance.customStyle[this.state]; + if (stateCustomStyle?.overrideMainColor && stateCustomStyle?.mainColor) { + mainColor = stateCustomStyle.mainColor; + } + if (stateCustomStyle?.overrideBackgroundColor && stateCustomStyle?.backgroundColor) { + backgroundColor = stateCustomStyle.backgroundColor; + } + if (stateCustomStyle?.overrideBorderColor && stateCustomStyle?.borderColor) { + borderColor = stateCustomStyle.borderColor; + } + return { + mainColor, + backgroundColor, + borderColor + } + } + + protected abstract get state(): WidgetButtonToggleState; + + protected getMainColor(appearance: SegmentedButtonStyles): string { + return appearance.mainColor || defaultMainColor; + } + + protected getBackgroundColor(appearance: SegmentedButtonStyles): string { + return appearance.backgroundColor || defaultBackgroundColor; + } + + protected getBorderColor(): string { + return defaultBorderColor; + } +} + +class EnabledButtonStateCssGenerator extends ButtonToggleStateCssGenerator { + + protected get state(): WidgetButtonToggleState { + return WidgetButtonToggleState.enabled; + } +} + +class HoveredButtonStateCssGenerator extends ButtonToggleStateCssGenerator { + + protected get state(): WidgetButtonToggleState { + return WidgetButtonToggleState.hovered; + } +} + +class DisabledButtonStateCssGenerator extends ButtonToggleStateCssGenerator { + + protected get state(): WidgetButtonToggleState { + return WidgetButtonToggleState.disabled; + } + + protected getMainColor(): string { + return defaultMainColorDisabled; + } + + protected getBackgroundColor(): string { + return defaultBackgroundColorDisabled; + } + + protected getBorderColor(): string { + return defaultBorderColorDisabled; + } +} + +const buttonToggleStateCssGeneratorsMap = new Map( + [ + [WidgetButtonToggleState.enabled, new EnabledButtonStateCssGenerator()], + [WidgetButtonToggleState.hovered, new HoveredButtonStateCssGenerator()], + [WidgetButtonToggleState.disabled, new DisabledButtonStateCssGenerator()] + ] +); + +const widgetButtonCssSelector = '.mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header'; + +export const generateWidgetButtonToggleAppearanceCss = (selectedAppearance: SegmentedButtonStyles, unselectedAppearance: SegmentedButtonStyles): string => { + let statesCss = ''; + for (const state of widgetButtonToggleStates) { + const generator = buttonToggleStateCssGeneratorsMap.get(state); + statesCss += `\n${generator.generateStateCss(selectedAppearance, unselectedAppearance)}`; + } + return `${widgetButtonCssSelector} {\n`+ + `${statesCss}\n`+ + `}`; +}; + +export const generateWidgetButtonToggleBorderLayout = (layout: SegmentedButtonLayout): string => { + return `${widgetButtonCssSelector} {\n`+ + `--tb-widget-button-toggle-border-radius: ${segmentedButtonLayoutBorder.get(layout)}\n`+ + `}`; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html new file mode 100644 index 0000000000..4cf82918cb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html @@ -0,0 +1,263 @@ + + +
+
widgets.action-button.behavior
+
+
widgets.button-state.initial-state
+ +
+ +
+
widgets.action-button.left-button-click
+ + +
+
+
widgets.action-button.right-button-click
+ + +
+ +
+
widgets.button-state.disabled-state
+ +
+
+
+
widget-config.appearance
+ + + + {{ segmentedButtonLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.button.auto-scale' | translate }} + +
+
+
widgets.segmented-button.card-border
+
+ + +
px
+
+ + +
+
+
+ +
+
+
widgets.segmented-button.button-appearance
+ + {{ 'widgets.segmented-button.left' | translate }} + {{ 'widgets.segmented-button.right' | translate }} + +
+
+
+ + {{ 'widgets.button.label' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.button.icon' | translate }} + +
+ + + + + + +
+
+
+ +
+
+ + {{ 'widgets.button.label' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.button.icon' | translate }} + +
+ + + + + + +
+
+
+
+ +
+
+
widgets.segmented-button.color-styles
+ + {{ 'widgets.segmented-button.selected' | translate }} + {{ 'widgets.segmented-button.unselected' | translate }} + +
+
+
+
{{ 'widgets.button.color-palette' | translate }}
+
+
+
widgets.button.main
+ + +
+ +
+
widgets.button.background
+ + +
+
+
+
+ + + +
widgets.button.custom-styles
+
+
+ +
+
{{ widgetButtonToggleStatesTranslationsMap.get(state) | translate }}
+ + +
+
+
+
+
+
+
+
{{ 'widgets.button.color-palette' | translate }}
+
+
+
widgets.button.main
+ + +
+ +
+
widgets.button.background
+ + +
+
+
+
+ + + +
widgets.button.custom-styles
+
+
+ +
+
{{ widgetButtonToggleStatesTranslationsMap.get(state) | translate }}
+ + +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts new file mode 100644 index 0000000000..979cf2c078 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts @@ -0,0 +1,134 @@ +/// +/// 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 { + SegmentedButtonAppearanceType, + SegmentedButtonColorStylesType, + segmentedButtonDefaultSettings, + segmentedButtonLayoutBorder, + segmentedButtonLayoutImages, + segmentedButtonLayouts, + segmentedButtonLayoutTranslations, + WidgetButtonToggleState, + widgetButtonToggleStatesTranslations +} from '@home/components/widget/lib/button/segmented-button-widget.models'; + +@Component({ + selector: 'tb-segmented-button-widget-settings', + templateUrl: './segmented-button-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class SegmentedButtonWidgetSettingsComponent 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.segmentedButtonLayoutBorderMap.get(this.segmentedButtonWidgetSettingsForm.get('appearance.layout').value); + } + + segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'left'; + segmentedButtonColorStylesType: SegmentedButtonColorStylesType = 'selected'; + + widgetButtonToggleStates = Object.keys(WidgetButtonToggleState) as WidgetButtonToggleState[]; + widgetButtonToggleStatesTranslationsMap = widgetButtonToggleStatesTranslations; + + segmentedButtonLayouts = segmentedButtonLayouts; + segmentedButtonLayoutTranslationMap = segmentedButtonLayoutTranslations; + segmentedButtonLayoutImageMap = segmentedButtonLayoutImages; + segmentedButtonLayoutBorderMap = segmentedButtonLayoutBorder; + + valueType = ValueType; + + segmentedButtonWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.segmentedButtonWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...segmentedButtonDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.segmentedButtonWidgetSettingsForm = this.fb.group({ + initialState: [settings.initialState, []], + leftButtonClick: [settings.leftButtonClick, []], + rightButtonClick: [settings.rightButtonClick, []], + disabledState: [settings.disabledState, []], + + appearance: this.fb.group({ + layout: [settings.appearance.layout, []], + autoScale: [settings.appearance.autoScale, []], + cardBorder: [settings.appearance.cardBorder, []], + cardBorderColor: [settings.appearance.cardBorderColor, []], + leftAppearance: this.fb.group({ + showLabel: [settings.appearance.leftAppearance.showLabel, []], + label: [settings.appearance.leftAppearance.label, []], + labelFont: [settings.appearance.leftAppearance.labelFont, []], + showIcon: [settings.appearance.leftAppearance.showIcon, []], + icon: [settings.appearance.leftAppearance.icon, []], + iconSize: [settings.appearance.leftAppearance.iconSize, []], + iconSizeUnit: [settings.appearance.leftAppearance.iconSizeUnit, []], + }), + rightAppearance: this.fb.group({ + showLabel: [settings.appearance.rightAppearance.showLabel, []], + label: [settings.appearance.rightAppearance.label, []], + labelFont: [settings.appearance.rightAppearance.labelFont, []], + showIcon: [settings.appearance.rightAppearance.showIcon, []], + icon: [settings.appearance.rightAppearance.icon, []], + iconSize: [settings.appearance.rightAppearance.iconSize, []], + iconSizeUnit: [settings.appearance.rightAppearance.iconSizeUnit, []], + }), + selectedStyle: this.fb.group({ + mainColor: [settings.appearance.selectedStyle.mainColor, []], + backgroundColor: [settings.appearance.selectedStyle.backgroundColor, []], + customStyle: this.fb.group({ + enabled: [settings.appearance.selectedStyle.customStyle.enabled, []], + hovered: [settings.appearance.selectedStyle.customStyle.hovered, []], + disabled: [settings.appearance.selectedStyle.customStyle.disabled, []], + }) + }), + unselectedStyle: this.fb.group({ + mainColor: [settings.appearance.unselectedStyle.mainColor, []], + backgroundColor: [settings.appearance.unselectedStyle.backgroundColor, []], + customStyle: this.fb.group({ + enabled: [settings.appearance.unselectedStyle.customStyle.enabled, []], + hovered: [settings.appearance.unselectedStyle.customStyle.hovered, []], + disabled: [settings.appearance.unselectedStyle.customStyle.disabled, []], + }) + }), + }) + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.html new file mode 100644 index 0000000000..f0e50b03e6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.html @@ -0,0 +1,92 @@ + +
+
{{ widgetButtonToggleStatesTranslationsMap.get(state) | translate }}
+
+
+ + {{ 'widgets.button.main' | translate }} + + + +
+
+ + {{ 'widgets.button.background' | translate }} + + + +
+
+ + {{ 'widgets.button.border' | translate }} + + + +
+
+
+ widgets.button.preview +
+ + +
+
+
+ + + +
+ +
+
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-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-toggle-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-toggle-custom-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts new file mode 100644 index 0000000000..98ced2dc2e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component.ts @@ -0,0 +1,210 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + defaultBackgroundColorDisabled, + defaultMainColorDisabled +} from '@shared/components/button/widget-button.models'; +import { merge } from 'rxjs'; +import { deepClone } from '@core/utils'; +import { WidgetButtonToggleComponent } from '@shared/components/button/widget-button-toggle.component'; +import { + ButtonToggleAppearance, + SegmentedButtonStyles, + WidgetButtonToggleCustomStyle, + WidgetButtonToggleState, + widgetButtonToggleStates, + widgetButtonToggleStatesTranslations +} from '@home/components/widget/lib/button/segmented-button-widget.models'; + +@Component({ + selector: 'tb-widget-button-toggle-custom-style-panel', + templateUrl: './widget-button-toggle-custom-style-panel.component.html', + providers: [], + styleUrls: ['./widget-button-toggle-custom-style-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonToggleCustomStylePanelComponent extends PageComponent implements OnInit { + + @ViewChild('widgetButtonTogglePreview') + widgetButtonTogglePreview: WidgetButtonToggleComponent; + + @Input() + appearance: ButtonToggleAppearance; + + @Input() + value: boolean = false; + + @Input() + borderRadius: string; + + @Input() + autoScale: boolean; + + @Input() + state: WidgetButtonToggleState; + + @Input() + customStyle: WidgetButtonToggleCustomStyle; + + private popoverValue: TbPopoverComponent; + + @Input() + set popover(popover: TbPopoverComponent) { + this.popoverValue = popover; + popover.tbAnimationDone.subscribe(() => { + this.widgetButtonTogglePreview?.validateSize(); + }); + } + + get popover(): TbPopoverComponent { + return this.popoverValue; + } + + @Output() + customStyleApplied = new EventEmitter(); + + widgetButtonToggleStatesTranslationsMap = widgetButtonToggleStatesTranslations; + + WidgetButtonToggleState = WidgetButtonToggleState; + + previewAppearance: ButtonToggleAppearance; + + copyFromStates: WidgetButtonToggleState[]; + + customStyleFormGroup: UntypedFormGroup; + + style: SegmentedButtonStyles; + + constructor(private fb: UntypedFormBuilder, + protected store: Store, + private cd: ChangeDetectorRef) { + super(store); + } + + ngOnInit(): void { + this.style = this.value ? this.appearance.selectedStyle : this.appearance.unselectedStyle; + this.copyFromStates = widgetButtonToggleStates.filter(state => + state !== this.state && !!this.style.customStyle[state]); + this.customStyleFormGroup = this.fb.group( + { + overrideMainColor: [false, []], + mainColor: [null, []], + overrideBackgroundColor: [false, []], + backgroundColor: [null, []], + overrideBorderColor: [false, []], + borderColor: [null, []] + } + ); + merge(this.customStyleFormGroup.get('overrideMainColor').valueChanges, + this.customStyleFormGroup.get('overrideBackgroundColor').valueChanges, + this.customStyleFormGroup.get('overrideBorderColor').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + this.customStyleFormGroup.valueChanges.subscribe(() => { + this.updatePreviewAppearance(); + }); + this.setStyle(this.customStyle); + } + + copyStyle(state: WidgetButtonToggleState) { + this.customStyle = deepClone(this.style[state]); + this.setStyle(this.customStyle); + this.customStyleFormGroup.markAsDirty(); + } + + cancel() { + this.popover?.hide(); + } + + applyCustomStyle() { + const customStyle: SegmentedButtonStyles = this.customStyleFormGroup.value; + this.customStyleApplied.emit(customStyle); + } + + private setStyle(customStyle?: WidgetButtonToggleCustomStyle): void { + let mainColor = this.state === WidgetButtonToggleState.disabled ? defaultMainColorDisabled : this.style.mainColor; + if (customStyle?.overrideMainColor) { + mainColor = customStyle?.mainColor; + } + let backgroundColor = this.state === WidgetButtonToggleState.disabled ? defaultBackgroundColorDisabled : this.style.backgroundColor; + if (customStyle?.overrideBackgroundColor) { + backgroundColor = customStyle?.backgroundColor; + } + let borderColor = this.state === WidgetButtonToggleState.disabled ? defaultBackgroundColorDisabled : this.style.backgroundColor; + if (customStyle?.overrideBorderColor) { + borderColor = customStyle?.borderColor; + } + this.customStyleFormGroup.patchValue({ + overrideMainColor: customStyle?.overrideMainColor, + mainColor, + overrideBackgroundColor: customStyle?.overrideBackgroundColor, + backgroundColor, + overrideBorderColor: customStyle?.overrideBorderColor, + borderColor + }, {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 overrideBorderColor: boolean = this.customStyleFormGroup.get('overrideBorderColor').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 (overrideBorderColor) { + this.customStyleFormGroup.get('borderColor').enable({emitEvent: false}); + } else { + this.customStyleFormGroup.get('borderColor').disable({emitEvent: false}); + } + } + + private updatePreviewAppearance() { + this.previewAppearance = deepClone(this.appearance); + if (this.value) { + this.previewAppearance.selectedStyle.customStyle[this.state] = this.customStyleFormGroup.value; + } else { + this.previewAppearance.unselectedStyle.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-toggle-custom-style.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.html new file mode 100644 index 0000000000..624d17af16 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-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-toggle-custom-style.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.scss new file mode 100644 index 0000000000..6badf1f804 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-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-toggle { + width: 84px; + height: 100%; + } + @media #{$mat-gt-xs} { + width: 188px; + tb-widget-button-toggle { + width: 124px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.ts new file mode 100644 index 0000000000..366c78a0c6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component.ts @@ -0,0 +1,169 @@ +/// +/// 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 { TbPopoverService } from '@shared/components/popover.service'; +import { MatIconButton } from '@angular/material/button'; +import { deepClone } from '@core/utils'; +import { + ButtonToggleAppearance, + WidgetButtonToggleCustomStyle, + WidgetButtonToggleState +} from '@home/components/widget/lib/button/segmented-button-widget.models'; +import { + WidgetButtonToggleCustomStylePanelComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component'; + +@Component({ + selector: 'tb-widget-button-toggle-custom-style', + templateUrl: './widget-button-toggle-custom-style.component.html', + styleUrls: ['./widget-button-toggle-custom-style.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetButtonToggleCustomStyleComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class WidgetButtonToggleCustomStyleComponent implements OnInit, OnChanges, ControlValueAccessor { + + @Input() + disabled = false; + + @Input() + value = false; + + @Input() + appearance: ButtonToggleAppearance; + + @Input() + borderRadius: string; + + @Input() + autoScale: boolean; + + @Input() + state: WidgetButtonToggleState; + + widgetButtonToggleState = WidgetButtonToggleState; + + modelValue: WidgetButtonToggleCustomStyle; + + previewAppearance: ButtonToggleAppearance; + + 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: WidgetButtonToggleCustomStyle): 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, + autoScale: this.autoScale, + state: this.state, + value: this.value, + customStyle: this.modelValue + }; + const widgetButtonCustomStylePanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, WidgetButtonToggleCustomStylePanelComponent, + ['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: WidgetButtonToggleCustomStyle): void { + this.modelValue = value; + this.updatePreviewAppearance(); + this.propagateChange(this.modelValue); + } + + private updatePreviewAppearance() { + this.previewAppearance = deepClone(this.appearance); + if (this.modelValue) { + if (this.value) { + this.previewAppearance.selectedStyle.customStyle[this.state] = this.modelValue; + } else { + this.previewAppearance.unselectedStyle.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 fbfffc973f..037096f406 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 @@ -158,6 +158,12 @@ import { import { ScadaSymbolObjectSettingsComponent } from '@home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component'; +import { + WidgetButtonToggleCustomStyleComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style.component'; +import { + WidgetButtonToggleCustomStylePanelComponent +} from '@home/components/widget/lib/settings/common/button/widget-button-toggle-custom-style-panel.component'; @NgModule({ declarations: [ @@ -196,7 +202,9 @@ import { WidgetActionSettingsPanelComponent, WidgetButtonAppearanceComponent, WidgetButtonCustomStyleComponent, + WidgetButtonToggleCustomStyleComponent, WidgetButtonCustomStylePanelComponent, + WidgetButtonToggleCustomStylePanelComponent, TimeSeriesChartAxisSettingsComponent, TimeSeriesChartThresholdsPanelComponent, TimeSeriesChartThresholdRowComponent, @@ -261,7 +269,9 @@ import { WidgetActionSettingsPanelComponent, WidgetButtonAppearanceComponent, WidgetButtonCustomStyleComponent, + WidgetButtonToggleCustomStyleComponent, WidgetButtonCustomStylePanelComponent, + WidgetButtonToggleCustomStylePanelComponent, TimeSeriesChartAxisSettingsComponent, TimeSeriesChartThresholdsPanelComponent, TimeSeriesChartThresholdRowComponent, 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 37f61b11fb..4b5f3a1ad0 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 @@ -368,6 +368,9 @@ import { import { ScadaSymbolWidgetSettingsComponent } from '@home/components/widget/lib/settings/scada/scada-symbol-widget-settings.component'; +import { + SegmentedButtonWidgetSettingsComponent +} from '@home/components/widget/lib/settings/button/segmented-button-widget-settings.component'; @NgModule({ declarations: [ @@ -483,6 +486,7 @@ ScadaSymbolWidgetSettingsComponent BarChartWithLabelsWidgetSettingsComponent, SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, + SegmentedButtonWidgetSettingsComponent, CommandButtonWidgetSettingsComponent, PowerButtonWidgetSettingsComponent, SliderWidgetSettingsComponent, @@ -619,6 +623,7 @@ ScadaSymbolWidgetSettingsComponent BarChartWithLabelsWidgetSettingsComponent, SingleSwitchWidgetSettingsComponent, ActionButtonWidgetSettingsComponent, + SegmentedButtonWidgetSettingsComponent, CommandButtonWidgetSettingsComponent, PowerButtonWidgetSettingsComponent, SliderWidgetSettingsComponent, @@ -723,6 +728,7 @@ export const widgetSettingsComponentsMap: {[key: string]: Type +
+ + +
+ {{ appearance.leftAppearance.icon }} + {{ leftLabel$ | async }} +
+
+ +
+ {{ appearance.rightAppearance.icon }} + {{ rightLabel$ | async }} +
+
+
+
diff --git a/ui-ngx/src/app/shared/components/button/widget-button-toggle.component.scss b/ui-ngx/src/app/shared/components/button/widget-button-toggle.component.scss new file mode 100644 index 0000000000..4ce8c69ddb --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button-toggle.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. + */ + +$defaultBorderRadius: 4px; + +$defaultCheckedMainColor: #FFFFFF; +$defaultCheckedBackgroundColor: #305680; +$defaultCheckedBorderColor: #305680; + +$defaultUncheckedMainColor: rgba(0, 0, 0, 0.76); +$defaultUncheckedBackgroundColor: #E8E8E8; +$defaultUncheckedBorderColor: #E8E8E8; + +$defaultCheckedMainColorDisabled: #FFFFFFCC; +$defaultCheckedBackgroundColorDisabled: rgba(0, 0, 0, 0.12); +$defaultCheckedBorderColorDisabled: rgba(0, 0, 0, 0.12); + +$defaultUncheckedMainColorDisabled: rgba(0, 0, 0, 0.12); +$defaultUncheckedBackgroundColorDisabled: #E8E8E8; +$defaultUncheckedBorderColorDisabled: #E8E8E8; + +$buttonBorderRadius: var(--tb-widget-button-toggle-border-radius, $defaultBorderRadius); + +$mainCheckedColorEnabled: var(--tb-widget-button-toggle-main-checked-color-enabled, $defaultCheckedMainColor); +$backgroundCheckedColorEnabled: var(--tb-widget-button-toggle-background-checked-color-enabled, $defaultCheckedBackgroundColor); +$borderCheckedColorEnabled: var(--tb-widget-button-toggle-border-checked-color-enabled, $defaultCheckedBorderColor); + +$mainUncheckedColorEnabled: var(--tb-widget-button-toggle-main-unchecked-color-enabled, $defaultUncheckedMainColor); +$backgroundUncheckedColorEnabled: var(--tb-widget-button-toggle-background-unchecked-color-enabled, $defaultUncheckedBackgroundColor); +$borderUncheckedColorEnabled: var(--tb-widget-button-toggle-border-unchecked-color-enabled, $defaultUncheckedBorderColor); + +$mainCheckedColorDisabled: var(--tb-widget-button-toggle-main-checked-color-disabled, $defaultCheckedMainColorDisabled); +$backgroundCheckedColorDisabled: var(--tb-widget-button-toggle-background-checked-color-disabled, $defaultCheckedBackgroundColorDisabled); +$borderCheckedColorDisabled: var(--tb-widget-button-toggle-border-checked-color-disabled, $defaultCheckedBorderColorDisabled); + +$mainUncheckedColorDisabled: var(--tb-widget-button-toggle-main-unchecked-color-disabled, $defaultUncheckedMainColorDisabled); +$backgroundUncheckedColorDisabled: var(--tb-widget-button-toggle-background-unchecked-color-disabled, $defaultUncheckedBackgroundColorDisabled); +$borderUncheckedColorDisabled: var(--tb-widget-button-toggle-border-unchecked-color-disabled, $defaultUncheckedBorderColorDisabled); + +$mainCheckedColorHovered: var(--tb-widget-button-toggle-main-checked-color-hovered, $defaultCheckedMainColor); +$backgroundCheckedColorHovered: var(--tb-widget-button-toggle-background-checked-color-hovered, $defaultCheckedBackgroundColor); +$borderCheckedColorHovered: var(--tb-widget-button-toggle-border-checked-color-hovered, $defaultCheckedBorderColor); + +$mainUncheckedColorHovered: var(--tb-widget-button-toggle-main-unchecked-color-hovered, $defaultCheckedMainColor); +$backgroundUncheckedColorHovered: var(--tb-widget-button-toggle-background-unchecked-color-hovered, $defaultCheckedBackgroundColor); +$borderUncheckedColorHovered: var(--tb-widget-button-toggle-border-unchecked-color-hovered, $defaultCheckedBorderColor); + +@mixin _tb-widget-button-styles($main, $background, $boxShadow) { + color: $main; + background-color: $background; + border: 1px solid $boxShadow; +} + +:host { + max-width: 100%; + .tb-toggle-container { + display: block; + height: 100%; + } + .tb-toggle-header { + transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1); + } + + .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; + } + } +} + +:host ::ng-deep { + .mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header { + overflow: visible; + width: 100%; + height: 100%; + padding: 3px; + border: 1px solid; + background: transparent; + .mat-button-toggle + .mat-button-toggle { + border-left: none; + } + .mat-button-toggle.mat-button-toggle-appearance-standard { + flex: 1; + width: 50%; + border-radius: $buttonBorderRadius; + + &.tb-hover-state { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainUncheckedColorHovered, $backgroundUncheckedColorHovered, $borderUncheckedColorHovered); + } + &.mat-button-toggle-checked { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainCheckedColorHovered, $backgroundCheckedColorHovered, $borderCheckedColorHovered); + } + } + } + + .mat-button-toggle-button { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + border-radius: $buttonBorderRadius; + @include _tb-widget-button-styles($mainUncheckedColorEnabled, $backgroundUncheckedColorEnabled, $borderUncheckedColorEnabled); + .mat-button-toggle-label-content { + .mat-pseudo-checkbox { + display: none; + } + } + } + &.mat-button-toggle-checked { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainCheckedColorEnabled, $backgroundCheckedColorEnabled, $borderCheckedColorEnabled); + + } + } + + } + &.tb-pointer-events { + .mat-button-toggle.mat-button-toggle-appearance-standard { + .mat-button-toggle-button { + pointer-events: none; + } + .mat-button-toggle-focus-overlay, .mat-button-toggle-ripple { + opacity: 0; + } + } + } + &:not(:disabled):not(.tb-disabled-state):not(.tb-pointer-events) { + .mat-button-toggle.mat-button-toggle-appearance-standard { + &:hover { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainUncheckedColorHovered, $backgroundUncheckedColorHovered, $borderUncheckedColorHovered); + } + &.mat-button-toggle-checked { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainCheckedColorHovered, $backgroundCheckedColorHovered, $borderCheckedColorHovered); + } + } + } + } + } + &:disabled, &.tb-disabled-state { + pointer-events: none; + .mat-button-toggle.mat-button-toggle-appearance-standard { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainUncheckedColorDisabled, $backgroundUncheckedColorDisabled, $borderUncheckedColorDisabled); + } + + &.mat-button-toggle-checked { + .mat-button-toggle-button { + @include _tb-widget-button-styles($mainCheckedColorDisabled, $backgroundCheckedColorDisabled, $borderCheckedColorDisabled); + } + } + } + .mat-button-toggle-focus-overlay, .mat-button-toggle-ripple { + opacity: 0; + } + } + .mat-button-toggle-focus-overlay, .mat-button-toggle-ripple { + border-radius: inherit; + } + } +} diff --git a/ui-ngx/src/app/shared/components/button/widget-button-toggle.component.ts b/ui-ngx/src/app/shared/components/button/widget-button-toggle.component.ts new file mode 100644 index 0000000000..4f281b2aa0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/widget-button-toggle.component.ts @@ -0,0 +1,234 @@ +/// +/// 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 +} from '@angular/core'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ComponentStyle, iconStyle, textStyle, validateCssSize } from '@shared/models/widget-settings.models'; +import { UtilsService } from '@core/services/utils.service'; +import { Observable, of } from 'rxjs'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { + generateWidgetButtonToggleAppearanceCss, + generateWidgetButtonToggleBorderLayout, + segmentedButtonDefaultAppearance, + segmentedButtonLayoutBorder +} from '@home/components/widget/lib/button/segmented-button-widget.models'; +import { MatButtonToggleChange } from '@angular/material/button-toggle'; + +const initialButtonHeight = 60; +const horizontalLayoutPadding = 10; + +@Component({ + selector: 'tb-widget-button-toggle', + templateUrl: './widget-button-toggle.component.html', + styleUrls: ['./widget-button-toggle.component.scss'] +}) +export class WidgetButtonToggleComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges { + + @ViewChild('toggleGroupContainer', {static: false}) + toggleGroupContainer: ElementRef; + + @ViewChild('widgetButton', {read: ElementRef}) + widgetButton: ElementRef; + + @ViewChild('leftButtonContent', {static: false}) + leftButtonContent: ElementRef; + @ViewChild('rightButtonContent', {static: false}) + rightButtonContent: ElementRef; + + @Input() + appearance = segmentedButtonDefaultAppearance; + + @Input() + borderRadius: string; + + @Input() + autoScale: boolean; + + @Input() + @coerceBoolean() + value = false; + + @Input() + @coerceBoolean() + disabled = false; + + @Input() + @coerceBoolean() + hovered = false; + + @Input() + @coerceBoolean() + disableEvents = false; + + @Input() + ctx: WidgetContext; + + @Output() + clicked = new EventEmitter(); + + leftLabel$: Observable; + rightLabel$: Observable; + + leftIconStyle: ComponentStyle = {}; + rightIconStyle: ComponentStyle = {}; + + leftLabelStyle: ComponentStyle = {}; + rightLabelStyle: ComponentStyle = {}; + + computedBorderColor: string; + computedBorderWidth: string; + computedBorderRadius: string; + + 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(); + } else if (propName === 'borderRadius') { + this.updateBorderRadius(); + } else if (propName === 'autoScale') { + this.updateAutoScale(); + } + } + } + } + + ngAfterViewInit(): void { + this.updateAutoScale(); + } + + ngOnDestroy(): void { + if (this.buttonResize$) { + this.buttonResize$.disconnect(); + } + this.clearAppearanceCss(); + } + + public validateSize() { + if (this.appearance.autoScale && this.widgetButton.nativeElement) { + this.onResize(); + } + } + + private updateAppearance(): void { + this.clearAppearanceCss(); + this.computedBorderColor = this.appearance.cardBorderColor; + this.computedBorderWidth = validateCssSize(this.appearance.cardBorder.toString()); + if (this.appearance.leftAppearance.showIcon) { + this.leftIconStyle = iconStyle(this.appearance.leftAppearance.iconSize, this.appearance.leftAppearance.iconSizeUnit); + } + if (this.appearance.rightAppearance.showIcon) { + this.rightIconStyle = iconStyle(this.appearance.rightAppearance.iconSize, this.appearance.rightAppearance.iconSizeUnit); + } + if (this.appearance.leftAppearance.showLabel) { + this.leftLabelStyle = textStyle(this.appearance.leftAppearance.labelFont); + this.leftLabel$ = this.ctx ? this.ctx.registerLabelPattern(this.appearance.leftAppearance.label, this.leftLabel$) : of(this.appearance.leftAppearance.label); + } + if (this.appearance.rightAppearance.showLabel) { + this.rightLabelStyle = textStyle(this.appearance.rightAppearance.labelFont); + this.rightLabel$ = this.ctx ? this.ctx.registerLabelPattern(this.appearance.rightAppearance.label, this.rightLabel$) : of(this.appearance.rightAppearance.label); + } + this.updateBorderRadius(); + const appearanceCss = generateWidgetButtonToggleAppearanceCss(this.appearance.selectedStyle, this.appearance.unselectedStyle); + const layoutCss = generateWidgetButtonToggleBorderLayout(this.appearance.layout); + this.appearanceCssClass = this.utils.applyCssToElement(this.renderer, this.elementRef.nativeElement, + 'tb-widget-button', appearanceCss + layoutCss); + this.updateAutoScale(); + } + + private updateBorderRadius(): void { + if (this.borderRadius?.length) { + const validatedBorderRadius = validateCssSize(this.borderRadius); + if (validatedBorderRadius) { + this.computedBorderRadius = validatedBorderRadius; + } else { + this.computedBorderRadius = this.borderRadius; + } + } else { + this.computedBorderRadius = segmentedButtonLayoutBorder.get(this.appearance.layout); + } + + } + + 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.rightButtonContent && this.leftButtonContent) { + const autoScale = isDefinedAndNotNull(this.autoScale) ? this.autoScale : this.appearance.autoScale; + if (autoScale) { + this.buttonResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.buttonResize$.observe(this.widgetButton.nativeElement); + this.onResize(); + } else { + this.renderer.setStyle(this.widgetButton.nativeElement, 'transform', 'none'); + this.renderer.setStyle(this.widgetButton.nativeElement, 'width', '100%'); + } + } + } + + private onResize() { + const height = this.widgetButton.nativeElement.getBoundingClientRect().height; + const buttonScale = height / initialButtonHeight; + const buttonWidth = this.widgetButton.nativeElement.getBoundingClientRect().width; + const buttonHeight = this.widgetButton.nativeElement.getBoundingClientRect().height; + this.renderer.setStyle(this.leftButtonContent.nativeElement, 'transform', `scale(1)`); + this.renderer.setStyle(this.rightButtonContent.nativeElement, 'transform', `scale(1)`); + const contentWidth = this.leftButtonContent.nativeElement.getBoundingClientRect().width; + const contentHeight = this.leftButtonContent.nativeElement.getBoundingClientRect().height; + const maxScale = Math.max(1, buttonScale); + const scale = Math.min(Math.min((buttonWidth / 2 - horizontalLayoutPadding) / contentWidth, buttonHeight / contentHeight), maxScale); + this.renderer.setStyle(this.leftButtonContent.nativeElement, 'transform', `scale(${scale})`); + this.renderer.setStyle(this.rightButtonContent.nativeElement, 'transform', `scale(${scale})`); + } +} diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 02b5817800..07eb13f25d 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -218,6 +218,7 @@ import { CountryAutocompleteComponent } from '@shared/components/country-autocom import { CountryData } from '@shared/models/country.models'; import { SvgXmlComponent } from '@shared/components/svg-xml.component'; import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component'; +import { WidgetButtonToggleComponent } from '@shared/components/button/widget-button-toggle.component'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -419,6 +420,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) EmbedImageDialogComponent, ImageGalleryDialogComponent, WidgetButtonComponent, + WidgetButtonToggleComponent, HexInputComponent, ScadaSymbolInputComponent ], @@ -676,6 +678,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) EmbedImageDialogComponent, ImageGalleryDialogComponent, WidgetButtonComponent, + WidgetButtonToggleComponent, ScadaSymbolInputComponent ] }) 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 405f4ae9d3..4619b14112 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5621,7 +5621,12 @@ "action-button": { "behavior": "Behavior", "on-click": "On click", - "on-click-hint": "Action triggered when the button is clicked" + "on-click-hint": "Action triggered when the button is clicked", + "left-button-click": "Left button click", + "left-button-click-hint": "Action while pressing on left button.", + "right-button-click": "Right button click", + "right-button-click-hint": "Action while pressing on right button.", + "button-click-hint": "Action while pressing on widget." }, "command-button": { "behavior": "Behavior", @@ -5666,6 +5671,18 @@ "vertical-fill": "Vertical fill", "button-appearance": "Button appearance" }, + "segmented-button": { + "layout": "Layout", + "layout-squared": "Squared", + "layout-rounded": "Rounded", + "card-border": "Card border", + "button-appearance": "Button appearance", + "left": "Left", + "right": "Right", + "color-styles": "Color styles", + "selected": "Selected", + "unselected": "Unselected" + }, "button": { "layout": "Layout", "outlined": "Outlined", @@ -5679,6 +5696,7 @@ "color-palette": "Color palette", "main": "Main", "background": "Background", + "border": "Border", "custom-styles": "Custom styles", "clear-style": "Clear style", "shadow": "Shadow", @@ -5692,11 +5710,16 @@ "activated-state-hint": "Configure condition under which the button is active.", "disabled-state": "Disabled state", "disabled-state-hint": "Configure condition under which the button is disabled.", + "initial-state": "Initial button", + "initial-state-hint": "Action to get the initial state of the component", "enabled": "Enabled", "hovered": "Hovered", "pressed": "Pressed", "activated": "Activated", - "disabled": "Disabled" + "disabled": "Disabled", + "initial": "Left button", + "left": "Left", + "right": "Right" }, "background": { "background": "Background", diff --git a/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg b/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg new file mode 100644 index 0000000000..2a0945030b --- /dev/null +++ b/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg b/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg new file mode 100644 index 0000000000..2c1d283e9a --- /dev/null +++ b/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg @@ -0,0 +1 @@ + \ No newline at end of file From a9fe888223da9e9ddef920e89ecf6b07329780f8 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 28 Nov 2024 19:01:48 +0200 Subject: [PATCH 003/103] UI: Update layout image --- ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg | 2 +- ui-ngx/src/assets/widget/segmented-button/squared-layout.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg b/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg index 2a0945030b..6f0963016c 100644 --- a/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg +++ b/ui-ngx/src/assets/widget/segmented-button/rounded-layout.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg b/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg index 2c1d283e9a..6fb5abd30b 100644 --- a/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg +++ b/ui-ngx/src/assets/widget/segmented-button/squared-layout.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 244bb8cd1ed7c5546a4eca5a1356ef123241f044 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 28 Nov 2024 19:36:49 +0200 Subject: [PATCH 004/103] UI: Refactoring --- .../main/data/json/system/widget_types/segmented_button.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_types/segmented_button.json b/application/src/main/data/json/system/widget_types/segmented_button.json index 55e1165b83..967ed82315 100644 --- a/application/src/main/data/json/system/widget_types/segmented_button.json +++ b/application/src/main/data/json/system/widget_types/segmented_button.json @@ -22,7 +22,7 @@ "resources": [ { "link": "/api/images/system/segmented-button.svg", - "title": "\"Action button\" system widget image", + "title": "\"Segmented button\" system widget image", "type": "IMAGE", "subType": "IMAGE", "fileName": "segmented-button.svg", From c42cf2817e3b3728f5dca48933a41ffd9787518f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 2 Dec 2024 17:51:19 +0200 Subject: [PATCH 005/103] Housekeeper: interrupt task processing on timeout --- .../housekeeper/HousekeeperService.java | 6 +- .../processor/HousekeeperTaskProcessor.java | 11 ++++ .../LatestTsDeletionTaskProcessor.java | 2 +- .../TsHistoryDeletionTaskProcessor.java | 2 +- .../housekeeper/HousekeeperServiceTest.java | 58 ++++++++++++++++++- .../engine/util/SemaphoreWithTbMsgQueue.java | 2 +- 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java index 933f6fad3e..c69bc62692 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java @@ -117,9 +117,10 @@ public class HousekeeperService { throw new IllegalArgumentException("Unsupported task type " + taskType); } + Future future = null; try { long startTs = System.currentTimeMillis(); - Future future = taskExecutor.submit(() -> { + future = taskExecutor.submit(() -> { taskProcessor.process((T) task); return null; }); @@ -137,7 +138,8 @@ public class HousekeeperService { if (e instanceof ExecutionException) { error = e.getCause(); } else if (e instanceof TimeoutException) { - error = new TimeoutException("Timeout after " + config.getTaskProcessingTimeout() + " seconds"); + future.cancel(true); // interrupting the task + error = new TimeoutException("Timeout after " + config.getTaskProcessingTimeout() + " ms"); } if (msg.getTask().getAttempt() < config.getMaxReprocessingAttempts()) { diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java index f193b154db..5ef8b2f7f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java @@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; import org.thingsboard.server.common.msg.housekeeper.HousekeeperClient; +import java.util.concurrent.Future; + public abstract class HousekeeperTaskProcessor { @Autowired @@ -29,4 +31,13 @@ public abstract class HousekeeperTaskProcessor { public abstract HousekeeperTaskType getTaskType(); + public V wait(Future future) throws Exception { + try { + return future.get(); // will be interrupted after taskProcessingTimeout + } catch (InterruptedException e) { + future.cancel(true); // interrupting the underlying task + throw e; + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java index 1f4b6b57ae..64d0043c5a 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/LatestTsDeletionTaskProcessor.java @@ -33,7 +33,7 @@ public class LatestTsDeletionTaskProcessor extends HousekeeperTaskProcessor { + Future future = someExecutor.submit(() -> { + try { + Thread.sleep(TimeUnit.HOURS.toMillis(24)); + } catch (InterruptedException e) { + underlyingTaskInterrupted.set(true); + } + }); + try { + future.get(); + } catch (InterruptedException e) { + taskInterrupted.set(true); + future.cancel(true); + throw e; + } + return null; + }).when(tsHistoryDeletionTaskProcessor).process(any()); + + Device device = createDevice("test", "test"); + createRelatedData(device.getId()); + + doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); + + int attempts = 2; + await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + for (int i = 0; i <= attempts; i++) { + int attempt = i; + verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, + task -> task.getAttempt() == attempt)), argThat(error -> error instanceof TimeoutException)); + } + }); + assertThat(taskInterrupted).isTrue(); + assertThat(underlyingTaskInterrupted).isTrue(); + + assertThat(getTimeseriesHistory(device.getId())).isNotEmpty(); + doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any()); + someExecutor.shutdown(); + + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(getTimeseriesHistory(device.getId())).isEmpty(); + }); + } + @Test public void whenReprocessingAttemptsExceeded_thenDropTheTask() throws Exception { TimeoutException error = new TimeoutException("Test timeout"); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java index fa00856b4b..50a109163c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/SemaphoreWithTbMsgQueue.java @@ -115,7 +115,7 @@ public class SemaphoreWithTbMsgQueue { } TbMsg msg = tbMsgTbContext.msg(); TbContext ctx = tbMsgTbContext.ctx(); - log.warn("[{}] Failed to process message: {}", entityId, msg, t); + log.debug("[{}] Failed to process message: {}", entityId, msg, t); ctx.tellFailure(msg, t); // you are not allowed to throw here, because queue will remain unprocessed continue; // We are probably the last who process the queue. We have to continue poll until get successful callback or queue is empty } From b0809bef1c006a8795afbba514b6b84157ead304 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 2 Dec 2024 18:18:56 +0100 Subject: [PATCH 006/103] updated versions due to vulnerabilities fix --- pom.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index a1d5f26ecf..c03465ba07 100755 --- a/pom.xml +++ b/pom.xml @@ -42,13 +42,13 @@ 4.0.2 2.4.0-b180830.0359 4.0.5 - 3.2.4 - 3.2.5 - 3.2.5 - 6.1.6 - 6.2.4 - 6.2.4 - 5.1.2 + 3.2.12 + 3.2.12 + 3.2.12 + 6.1.15 + 6.2.11 + 6.2.8 + 5.1.5 0.12.5 2.0.13 2.23.1 @@ -81,13 +81,13 @@ 2.0.1 5.6.0 3.9.2 - 3.25.3 + 3.25.5 1.63.0 1.2.4 1.18.32 1.2.5 1.2.5 - 4.1.109.Final + 4.1.115.Final 2.0.65.Final 1.1.18 1.7.1 @@ -110,7 +110,7 @@ - 3.7.0 + 3.7.1 8.10.1 3.5.3 2.2 @@ -122,7 +122,7 @@ 1.6.1 1.9.4 4.4 - 1.12.5 + 1.12.12 1.0.4TB 3.7.1 10.1.3 @@ -137,7 +137,7 @@ 4.2.1 2.7.3 1.5.6 - 5.10.2 + 5.10.5 5.15.0 1.3.0 1.2.7 @@ -147,7 +147,7 @@ 3.25.3 5.4.0 2.2 - 1.19.7 + 1.20.4 1.0.1 1.12 4.19.1 From dd02e44822b59a984442361864f7a1d22e94c446 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 3 Dec 2024 12:59:24 +0200 Subject: [PATCH 007/103] Housekeeper: cancel task future in finally block --- .../server/service/housekeeper/HousekeeperService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java index c69bc62692..cfa5eeac7e 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/HousekeeperService.java @@ -138,7 +138,6 @@ public class HousekeeperService { if (e instanceof ExecutionException) { error = e.getCause(); } else if (e instanceof TimeoutException) { - future.cancel(true); // interrupting the task error = new TimeoutException("Timeout after " + config.getTaskProcessingTimeout() + " ms"); } @@ -155,6 +154,10 @@ public class HousekeeperService { .build()); } statsService.ifPresent(statsService -> statsService.reportFailure(taskType, msg)); + } finally { + if (future != null && !future.isDone()) { + future.cancel(true); + } } } From 68bc840dbd2486b230000ba59e434877ef478fa7 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 3 Dec 2024 13:38:20 +0200 Subject: [PATCH 008/103] UI: Refactoring segmented button --- .../json/system/widget_bundles/buttons.json | 2 +- .../widget_bundles/control_widgets.json | 2 +- ...ed_button.json => two_segment_button.json} | 16 ++++++------- ...gmented-button-basic-config.component.html | 24 +++++++++---------- ...segmented-button-basic-config.component.ts | 2 +- .../button/segmented-button-widget.models.ts | 2 +- ... two-segment-button-widget.component.html} | 0 ... two-segment-button-widget.component.scss} | 0 ...=> two-segment-button-widget.component.ts} | 8 +++---- ...nted-button-widget-settings.component.html | 22 ++++++++--------- ...mented-button-widget-settings.component.ts | 2 +- .../widget/widget-components.module.ts | 6 ++--- ui-ngx/src/app/shared/shared.module.ts | 4 ++-- .../assets/locale/locale.constant-en_US.json | 22 ++++++++--------- 14 files changed, 56 insertions(+), 56 deletions(-) rename application/src/main/data/json/system/widget_types/{segmented_button.json => two_segment_button.json} (96%) rename ui-ngx/src/app/modules/home/components/widget/lib/button/{segmented-button-widget.component.html => two-segment-button-widget.component.html} (100%) rename ui-ngx/src/app/modules/home/components/widget/lib/button/{segmented-button-widget.component.scss => two-segment-button-widget.component.scss} (100%) rename ui-ngx/src/app/modules/home/components/widget/lib/button/{segmented-button-widget.component.ts => two-segment-button-widget.component.ts} (93%) 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 aaed9f73e1..facca89336 100644 --- a/application/src/main/data/json/system/widget_bundles/buttons.json +++ b/application/src/main/data/json/system/widget_bundles/buttons.json @@ -11,7 +11,7 @@ "action_button", "command_button", "toggle_button", - "segmented_button", + "two_segment_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 6bc4bdc830..c3dc219f5b 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,7 +11,7 @@ "single_switch", "command_button", "toggle_button", - "segmented_button", + "two_segment_button", "power_button", "slider", "control_widgets.switch_control", diff --git a/application/src/main/data/json/system/widget_types/segmented_button.json b/application/src/main/data/json/system/widget_types/two_segment_button.json similarity index 96% rename from application/src/main/data/json/system/widget_types/segmented_button.json rename to application/src/main/data/json/system/widget_types/two_segment_button.json index 967ed82315..96c7987e13 100644 --- a/application/src/main/data/json/system/widget_types/segmented_button.json +++ b/application/src/main/data/json/system/widget_types/two_segment_button.json @@ -1,15 +1,15 @@ { - "fqn": "segmented_button", - "name": "Segmented button", + "fqn": "two_segment_button", + "name": "Two segment button", "deprecated": false, - "image": "tb-image;/api/images/system/segmented-button.svg", + "image": "tb-image;/api/images/system/two-segment-button.svg", "description": "Facilitates 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 stat", "descriptor": { "type": "latest", "sizeX": 5, "sizeY": 1.5, "resources": [], - "templateHtml": "\n", + "templateHtml": "\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n maxDatasources: 1,\n maxDataKeys: 0,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '80px',\n embedTitlePanel: true,\n overflowVisible: true,\n hideDataSettings: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n", "settingsSchema": "", @@ -21,12 +21,12 @@ }, "resources": [ { - "link": "/api/images/system/segmented-button.svg", - "title": "\"Segmented button\" system widget image", + "link": "/api/images/system/two-segment-button.svg", + "title": "\"Two segment button\" system widget image", "type": "IMAGE", "subType": "IMAGE", - "fileName": "segmented-button.svg", - "publicResourceKey": "koboT6A2CqNyRYPTP3ENmjMM6P8KK1PP", + "fileName": "two-segment-button.svg", + "publicResourceKey": "FmYUNvspfrtAsh4lx8vu1fgDA1wBjoC9", "mediaType": "image/svg+xml", "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPHJlY3QgeD0iLjM4IiB5PSI1OC4yIiB3aWR0aD0iMTk5LjI1IiBoZWlnaHQ9IjQzLjYxIiByeD0iMi42MyIgc3Ryb2tlPSIjMzA1NjgwIiBzdHJva2Utd2lkdGg9Ii43NSIvPgogPHJlY3QgeD0iMy4wMSIgeT0iNjAuODMiIHdpZHRoPSI5Ni45OSIgaGVpZ2h0PSIzOC4zNSIgcng9IjEuNSIgZmlsbD0iIzMwNTY4MCIvPgogPHBhdGggZD0iTTI5LjI4IDc1LjUyVjgzSDI4di03LjQ4aDEuMjh6bTIuMzUgMHYxLjAyaC01Ljk2di0xLjAyaDUuOTZ6bTEuOTIgMi45OFY4M2gtMS4yM3YtNS41NmgxLjE4bC4wNSAxLjA2em0xLjctMS4xdjEuMTVhMi40NCAyLjQ0IDAgMCAwLTEuMDcuMDYgMS4wNiAxLjA2IDAgMCAwLS42NS42NWMtLjA2LjE2LS4xLjM0LS4xLjUzbC0uMjkuMDJjMC0uMzUuMDQtLjY3LjEtLjk3LjA3LS4zLjE4LS41Ni4zMS0uNzguMTQtLjIzLjMyLS40LjUzLS41M2ExLjQgMS40IDAgMCAxIC45OC0uMTdsLjIuMDR6bTMuOTIgNC40OHYtMi42NWMwLS4yLS4wNC0uMzctLjEtLjUxYS43Ni43NiAwIDAgMC0uMzQtLjM0IDEuMTIgMS4xMiAwIDAgMC0uNTQtLjExYy0uMiAwLS4zOC4wMy0uNTMuMWEuODUuODUgMCAwIDAtLjM0LjI4LjY3LjY3IDAgMCAwLS4xMi40aC0xLjI0YzAtLjIzLjA2LS40NC4xNi0uNjUuMS0uMi4yNi0uMzguNDYtLjU1LjItLjE2LjQ1LS4yOC43My0uMzguMjgtLjA5LjYtLjEzLjk0LS4xMy40MiAwIC44LjA3IDEuMTIuMi4zMi4xNS41OC4zNi43Ni42NC4xOS4yOC4yOC42NC4yOCAxLjA2djIuNDdjMCAuMjYuMDIuNDkuMDYuNjkuMDMuMi4wOS4zNy4xNS41MlY4M2gtMS4yN2EyLjIgMi4yIDAgMCAxLS4xMy0uNWMtLjAzLS4yMi0uMDUtLjQyLS4wNS0uNjJ6bS4xOC0yLjI2LjAxLjc2aC0uODljLS4yMyAwLS40My4wMy0uNi4wNy0uMTguMDQtLjMzLjEtLjQ0LjE5cy0uMi4xOC0uMjYuM2EuODcuODcgMCAwIDAtLjEuMzljMCAuMTUuMDQuMjguMTEuNC4wNy4xMy4xNy4yMi4zLjI5LjEzLjA3LjMuMS40OC4xYTEuMzYgMS4zNiAwIDAgMCAxLjEyLS41NGMuMS0uMTUuMTctLjMuMTctLjQ0bC40LjU1Yy0uMDQuMTQtLjEuMy0uMi40Ni0uMS4xNi0uMjQuMzEtLjQuNDZhMS45NCAxLjk0IDAgMCAxLTEuMzMuNWMtLjM2IDAtLjY5LS4wOC0uOTctLjIyLS4yOS0uMTUtLjUtLjM1LS42Ny0uNmExLjc3IDEuNzcgMCAwIDEtLjA4LTEuNjNjLjEyLS4yMi4yOC0uNDIuNS0uNTcuMjItLjE1LjQ4LS4yNy44LS4zNS4zMS0uMDguNjctLjEyIDEuMDgtLjEyaC45N3ptNS45NCAyLjIzVjc1LjFoMS4yNFY4M2gtMS4xMmwtLjEyLTEuMTV6bS0zLjYyLTEuNTd2LS4xYzAtLjQzLjA1LS44MS4xNS0xLjE2LjEtLjM1LjI0LS42NS40My0uOWExLjkgMS45IDAgMCAxIDEuNi0uNzggMS44NCAxLjg0IDAgMCAxIDEuNTMuNzZjLjE4LjIzLjMzLjUyLjQzLjg1LjExLjM0LjE4LjcuMjMgMS4xMXYuMzVjLS4wNS40LS4xMi43Ni0uMjMgMS4wOS0uMS4zMy0uMjUuNjEtLjQyLjg1YTEuODQgMS44NCAwIDAgMS0xLjU1Ljc1IDEuOTUgMS45NSAwIDAgMS0xLjU5LS44IDIuNzkgMi43OSAwIDAgMS0uNDMtLjljLS4xLS4zNC0uMTUtLjcyLS4xNS0xLjEyem0xLjI0LS4xdi4xYzAgLjI1LjAyLjUuMDcuNzEuMDQuMjIuMTIuNDEuMjIuNTguMS4xNy4yMi4zLjM4LjQuMTYuMDguMzYuMTMuNTguMTMuMjggMCAuNTEtLjA2LjctLjE4cy4zMi0uMy40Mi0uNWMuMS0uMjIuMTgtLjQ1LjIxLS43MXYtLjkzYy0uMDItLjItLjA2LS40LS4xMi0uNTdhMS41MSAxLjUxIDAgMCAwLS4yNi0uNDYgMS4yNCAxLjI0IDAgMCAwLS45NS0uNDJjLS4yMiAwLS40MS4wNS0uNTcuMTUtLjE1LjEtLjI4LjIyLS4zOS40LS4xLjE2LS4xNy4zNi0uMjIuNThzLS4wNy40Ni0uMDcuNzF6bTYuNDMtMi43NFY4M0g0OC4xdi01LjU2aDEuMjR6bS0xLjMyLTEuNDZjMC0uMTkuMDYtLjM1LjE4LS40N2EuNy43IDAgMCAxIC41My0uMTljLjIxIDAgLjM5LjA2LjUxLjIuMTMuMTEuMi4yNy4yLjQ2IDAgLjE4LS4wNy4zNC0uMi40NmEuNzEuNzEgMCAwIDEtLjUxLjE5LjcyLjcyIDAgMCAxLS41My0uMTkuNjMuNjMgMCAwIDEtLjE4LS40NnptNS40MiAxLjQ2di45SDUwLjN2LS45aDMuMTR6bS0yLjIzLTEuMzZoMS4yM3Y1LjM4YzAgLjE3LjAzLjMuMDguNC4wNS4wOS4xMi4xNS4yLjE4LjEuMDMuMi4wNC4zMi4wNGExLjkgMS45IDAgMCAwIC40NC0uMDR2Ljk0YTMuMTcgMy4xNyAwIDAgMS0uODIuMTJjLS4yOCAwLS41NC0uMDUtLjc2LS4xNS0uMjEtLjEtLjM5LS4yNy0uNS0uNWExLjk0IDEuOTQgMCAwIDEtLjItLjkxdi01LjQ2em00LjcgMS4zNlY4M2gtMS4yNXYtNS41NmgxLjI1em0tMS4zMy0xLjQ2YzAtLjE5LjA2LS4zNS4xOS0uNDdhLjcuNyAwIDAgMSAuNTItLjE5Yy4yMiAwIC40LjA2LjUyLjIuMTMuMTEuMTkuMjcuMTkuNDYgMCAuMTgtLjA2LjM0LS4yLjQ2YS43MS43MSAwIDAgMS0uNTEuMTkuNzIuNzIgMCAwIDEtLjUyLS4xOS42My42MyAwIDAgMS0uMTktLjQ2em0yLjYzIDQuM3YtLjEyYzAtLjQuMDYtLjc3LjE4LTEuMTEuMTItLjM1LjI4LS42NS41LS45LjIzLS4yNi41LS40Ni44Mi0uNi4zMi0uMTQuNjgtLjIxIDEuMDktLjIxLjQgMCAuNzcuMDcgMS4wOC4yMS4zMy4xNC42LjM0LjgyLjYuMjIuMjUuNC41NS41MS45LjEyLjM0LjE4LjcxLjE4IDEuMTF2LjEyYzAgLjQtLjA2Ljc3LS4xOCAxLjEyYTIuMzYgMi4zNiAwIDAgMS0yLjQgMS43Yy0uNDEgMC0uNzgtLjA3LTEuMS0uMmEyLjM2IDIuMzYgMCAwIDEtLjgxLS42Yy0uMjItLjI2LS40LS41Ni0uNTEtLjlhMy40NCAzLjQ0IDAgMCAxLS4xOC0xLjEyem0xLjI0LS4xMnYuMTJjMCAuMjUuMDMuNDkuMDguNzEuMDUuMjIuMTMuNDIuMjQuNTkuMTEuMTYuMjUuMy40Mi40LjE4LjA5LjM4LjE0LjYyLjE0YTEuMTggMS4xOCAwIDAgMCAxLjAxLS41NGMuMTEtLjE3LjItLjM3LjI0LS41OS4wNi0uMjIuMDktLjQ2LjA5LS43di0uMTNjMC0uMjQtLjAzLS40OC0uMDktLjdhMS44IDEuOCAwIDAgMC0uMjQtLjU5IDEuMTggMS4xOCAwIDAgMC0xLjAyLS41NWMtLjI0IDAtLjQ0LjA1LS42MS4xNS0uMTcuMS0uMy4yMy0uNDIuNC0uMS4xNy0uMTkuMzctLjI0LjYtLjA1LjIxLS4wOC40NS0uMDguN3ptNi4zOS0xLjUzVjgzSDYzLjZ2LTUuNTZoMS4xN2wuMDcgMS4xOXpNNjQuNjIgODBoLS40YzAtLjQuMDYtLjc2LjE2LTEuMDkuMTEtLjMzLjI2LS42LjQ2LS44NGEyIDIgMCAwIDEgMS42LS43NGMuMjcgMCAuNS4wMy43Mi4xLjIyLjA4LjQuMi41Ni4zNi4xNi4xNy4yOC4zOC4zNi42NS4wOS4yNi4xMy41OC4xMy45NlY4M2gtMS4yNXYtMy42YzAtLjI3LS4wNC0uNDgtLjEyLS42M2EuNjYuNjYgMCAwIDAtLjMzLS4zM2MtLjE0LS4wNy0uMzItLjEtLjU0LS4xYTEuMiAxLjIgMCAwIDAtMSAuNWMtLjEuMTUtLjIuMzItLjI2LjUzLS4wNi4yLS4xLjQxLS4xLjY0em04LjE3IDEuODd2LTIuNjVjMC0uMi0uMDMtLjM3LS4xLS41MWEuNzYuNzYgMCAwIDAtLjMzLS4zNCAxLjEyIDEuMTIgMCAwIDAtLjU1LS4xMWMtLjIgMC0uMzcuMDMtLjUyLjFhLjg1Ljg1IDAgMCAwLS4zNS4yOC42Ny42NyAwIDAgMC0uMTIuNEg2OS42YzAtLjIzLjA1LS40NC4xNi0uNjUuMS0uMi4yNi0uMzguNDYtLjU1LjItLjE2LjQ0LS4yOC43Mi0uMzguMjgtLjA5LjYtLjEzLjk1LS4xMy40MiAwIC43OS4wNyAxLjExLjIuMzMuMTUuNTguMzYuNzcuNjQuMTguMjguMjguNjQuMjggMS4wNnYyLjQ3YzAgLjI2LjAyLjQ5LjA1LjY5LjA0LjIuMDkuMzcuMTYuNTJWODNoLTEuMjdhMi4yIDIuMiAwIDAgMS0uMTQtLjVjLS4wMy0uMjItLjA1LS40Mi0uMDUtLjYyem0uMTgtMi4yNi4wMS43NmgtLjg4Yy0uMjMgMC0uNDQuMDMtLjYxLjA3LS4xOC4wNC0uMzIuMS0uNDQuMTlzLS4yLjE4LS4yNi4zYS44Ny44NyAwIDAgMC0uMDkuMzljMCAuMTUuMDQuMjguMS40LjA3LjEzLjE3LjIyLjMuMjkuMTQuMDcuMy4xLjQ5LjFhMS4zNiAxLjM2IDAgMCAwIDEuMTEtLjU0Yy4xMS0uMTUuMTctLjMuMTgtLjQ0bC40LjU1YTIuMiAyLjIgMCAwIDEtLjYuOTEgMS45NCAxLjk0IDAgMCAxLTEuMzQuNWMtLjM2IDAtLjY4LS4wNy0uOTctLjIxLS4yOC0uMTUtLjUtLjM1LS42Ni0uNmExLjc3IDEuNzcgMCAwIDEtLjA4LTEuNjNjLjExLS4yMi4yOC0uNDIuNS0uNTcuMjEtLjE1LjQ4LS4yNy44LS4zNS4zLS4wOC42Ny0uMTIgMS4wNy0uMTJoLjk3em0zLjg4LTQuNTJWODNINzUuNnYtNy45aDEuMjV6IiBmaWxsPSIjZmZmIi8+CiA8cGF0aCBkPSJNMTM2Ljg2IDc4LjY0djEuMDJoLTMuOTd2LTEuMDJoMy45N3ptLTMuNjUtMy4xMlY4M2gtMS4zdi03LjQ4aDEuM3ptNC42NCAwVjgzaC0xLjI4di03LjQ4aDEuMjh6bTIuOTMgMS45MlY4M2gtMS4yNHYtNS41NmgxLjI0em0tMS4zMy0xLjQ2YzAtLjE5LjA3LS4zNS4xOS0uNDcuMTMtLjEzLjMtLjE5LjUyLS4xOS4yMiAwIC40LjA2LjUyLjIuMTMuMTEuMi4yNy4yLjQ2IDAgLjE4LS4wNy4zNC0uMi40NmEuNzEuNzEgMCAwIDEtLjUyLjE5LjcyLjcyIDAgMCAxLS41Mi0uMTkuNjMuNjMgMCAwIDEtLjE5LS40NnptNS4zNyAzLjMzdi45OWgtMi43MnYtMWgyLjcyem00LjI0LjloLTEuOTV2LTEuMDJoMS45NGMuMzQgMCAuNjItLjA2LjgzLS4xNy4yLS4xLjM2LS4yNi40Ni0uNDUuMS0uMi4xNC0uNDIuMTQtLjY3IDAtLjI0LS4wNS0uNDYtLjE0LS42Ni0uMS0uMjEtLjI1LS4zOC0uNDYtLjVzLS40OS0uMi0uODItLjJoLTEuNTZWODNoLTEuMjl2LTcuNDhoMi44NWMuNTcgMCAxLjA3LjEgMS40Ny4zLjQuMi43Mi40OS45My44NS4yMS4zNS4zMi43Ni4zMiAxLjIyIDAgLjQ4LS4xLjktLjMyIDEuMjQtLjIxLjM1LS41Mi42Mi0uOTMuOC0uNC4xOS0uOS4yOC0xLjQ3LjI4em02LjMyIDIuOWMtLjQxIDAtLjc4LS4wNy0xLjExLS4yYTIuNDYgMi40NiAwIDAgMS0xLjM4LTEuNDQgMy4wMSAzLjAxIDAgMCAxLS4xOC0xLjA2di0uMmMwLS40NC4wNi0uODQuMTktMS4xOS4xMi0uMzUuMy0uNjUuNTMtLjkuMjItLjI2LjQ5LS40NS44LS41OC4zLS4xNC42NC0uMiAxLS4yLjQgMCAuNzUuMDYgMS4wNC4yLjMuMTMuNTUuMzIuNzQuNTYuMi4yNC4zNS41My40NS44Ni4xLjMzLjE1LjcuMTUgMS4xdi41M2gtNC4zdi0uODloMy4wN3YtLjFjMC0uMjItLjA1LS40My0uMTMtLjYyLS4wOC0uMi0uMi0uMzYtLjM3LS40OGExLjEgMS4xIDAgMCAwLS42NS0uMThjLS4yMSAwLS40LjA1LS41NS4xNC0uMTYuMDgtLjMuMi0uNC4zN3MtLjE5LjM2LS4yNS42YTMuNCAzLjQgMCAwIDAtLjA4Ljc3di4yYzAgLjI1LjAzLjQ4LjEuNjkuMDcuMi4xNy4zOC4zLjU0YTEuMzQgMS4zNCAwIDAgMCAxLjEuNWMuMyAwIC41Ny0uMDcuOC0uMTlzLjQzLS4yOS42LS41bC42Ni42MmEyLjM0IDIuMzQgMCAwIDEtMS4xNy44OWMtLjI4LjEtLjYuMTUtLjk2LjE1ek0xNjAgNzguNVY4M2gtMS4yNHYtNS41NmgxLjE5bC4wNSAxLjA2em0xLjctMS4xdjEuMTVhMi40NyAyLjQ3IDAgMCAwLTEuMDcuMDYgMS4wNSAxLjA1IDAgMCAwLS42NS42NWMtLjA2LjE2LS4xLjM0LS4xLjUzbC0uMjkuMDJjMC0uMzUuMDQtLjY3LjEtLjk3LjA3LS4zLjE4LS41Ni4zMS0uNzguMTQtLjIzLjMyLS40LjUzLS41M2ExLjQgMS40IDAgMCAxIC45Ny0uMTdsLjIuMDR6bTIuNzQgNS42aC0xLjI0di02LjFjMC0uNDEuMDgtLjc2LjIzLTEuMDQuMTYtLjI4LjM4LS41LjY3LS42NGEyLjU4IDIuNTggMCAwIDEgMS43NS0uMTNsLS4wMy45NmExLjA0IDEuMDQgMCAwIDAtLjk3LjA2LjcyLjcyIDAgMCAwLS4zMS4zYy0uMDcuMTMtLjEuMy0uMS41Vjgzem0xLjE0LTUuNTZ2LjloLTMuMjR2LS45aDMuMjR6IiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii43NiIvPgo8L3N2Zz4K", "public": true diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html index 267a61f83b..54f9cd6625 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.html @@ -27,12 +27,12 @@
widgets.action-button.behavior
-
widgets.button-state.initial-state
+
widgets.button-state.selected-state
-
widgets.action-button.left-button-click
+
widgets.action-button.first-button-click
-
widgets.action-button.right-button-click
+
widgets.action-button.second-button-click
@@ -110,11 +110,11 @@
widgets.segmented-button.button-appearance
- {{ 'widgets.segmented-button.left' | translate }} - {{ 'widgets.segmented-button.right' | translate }} + {{ 'widgets.segmented-button.first' | translate }} + {{ 'widgets.segmented-button.second' | translate }}
-
+
{{ 'widgets.button.label' | translate }} @@ -145,7 +145,7 @@
-
+
{{ 'widgets.button.label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts index 2b86d88a4a..cf9c830fe8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/button/segmented-button-basic-config.component.ts @@ -49,7 +49,7 @@ export class SegmentedButtonBasicConfigComponent extends BasicWidgetConfigCompon return getTargetDeviceFromDatasources(datasources); } - segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'left'; + segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'first'; segmentedButtonColorStylesType: SegmentedButtonColorStylesType = 'selected'; widgetButtonToggleStates = Object.keys(WidgetButtonToggleState) as WidgetButtonToggleState[]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts index 611812f244..3c0417a406 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.models.ts @@ -32,7 +32,7 @@ export enum SegmentedButtonLayout { rounded = 'rounded' } -export type SegmentedButtonAppearanceType = 'left' | 'right'; +export type SegmentedButtonAppearanceType = 'first' | 'second'; export type SegmentedButtonColorStylesType = 'selected' | 'unselected'; export const segmentedButtonLayouts = Object.keys(SegmentedButtonLayout) as SegmentedButtonLayout[]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.ts index 6c7ee3eef8..042f1518b6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/segmented-button-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.ts @@ -28,12 +28,12 @@ import { ComponentStyle } from '@shared/models/widget-settings.models'; import { MatButtonToggleChange } from '@angular/material/button-toggle'; @Component({ - selector: 'tb-segmented-button-widget', - templateUrl: './segmented-button-widget.component.html', - styleUrls: ['../action/action-widget.scss', './segmented-button-widget.component.scss'], + selector: 'tb-two-segment-button-widget', + templateUrl: './two-segment-button-widget.component.html', + styleUrls: ['../action/action-widget.scss', './two-segment-button-widget.component.scss'], encapsulation: ViewEncapsulation.None }) -export class SegmentedButtonWidgetComponent extends +export class TwoSegmentButtonWidgetComponent extends BasicActionWidgetComponent implements OnInit, AfterViewInit, OnDestroy { settings: SegmentedButtonWidgetSettings; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html index 4cf82918cb..e259919cc6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.html @@ -19,11 +19,11 @@
widgets.action-button.behavior
-
widgets.button-state.initial-state
+
widgets.button-state.selected-state
-
widgets.action-button.left-button-click
+
widgets.action-button.first-button-click
-
widgets.action-button.right-button-click
+
widgets.action-button.second-button-click
@@ -102,11 +102,11 @@
widgets.segmented-button.button-appearance
- {{ 'widgets.segmented-button.left' | translate }} - {{ 'widgets.segmented-button.right' | translate }} + {{ 'widgets.segmented-button.first' | translate }} + {{ 'widgets.segmented-button.second' | translate }}
-
+
{{ 'widgets.button.label' | translate }} @@ -137,7 +137,7 @@
-
+
{{ 'widgets.button.label' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts index 979cf2c078..7dab81ccad 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/button/segmented-button-widget-settings.component.ts @@ -52,7 +52,7 @@ export class SegmentedButtonWidgetSettingsComponent extends WidgetSettingsCompon return this.segmentedButtonLayoutBorderMap.get(this.segmentedButtonWidgetSettingsForm.get('appearance.layout').value); } - segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'left'; + segmentedButtonAppearanceType: SegmentedButtonAppearanceType = 'first'; segmentedButtonColorStylesType: SegmentedButtonColorStylesType = 'selected'; widgetButtonToggleStates = Object.keys(WidgetButtonToggleState) as WidgetButtonToggleState[]; 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 4488380804..21f2d89dc6 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 @@ -87,7 +87,7 @@ import { } from '@home/components/widget/lib/cards/notification-type-filter-panel.component'; import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component'; -import { SegmentedButtonWidgetComponent } from '@home/components/widget/lib/button/segmented-button-widget.component'; +import { TwoSegmentButtonWidgetComponent } from '@home/components/widget/lib/button/two-segment-button-widget.component'; @NgModule({ declarations: [ @@ -125,7 +125,7 @@ import { SegmentedButtonWidgetComponent } from '@home/components/widget/lib/butt BarChartWithLabelsWidgetComponent, SingleSwitchWidgetComponent, ActionButtonWidgetComponent, - SegmentedButtonWidgetComponent, + TwoSegmentButtonWidgetComponent, CommandButtonWidgetComponent, PowerButtonWidgetComponent, SliderWidgetComponent, @@ -187,7 +187,7 @@ import { SegmentedButtonWidgetComponent } from '@home/components/widget/lib/butt BarChartWithLabelsWidgetComponent, SingleSwitchWidgetComponent, ActionButtonWidgetComponent, - SegmentedButtonWidgetComponent, + TwoSegmentButtonWidgetComponent, CommandButtonWidgetComponent, PowerButtonWidgetComponent, SliderWidgetComponent, diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 07eb13f25d..424332ac45 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -384,6 +384,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) WidgetsBundleSearchComponent, CopyButtonComponent, TogglePasswordComponent, + WidgetButtonToggleComponent, ProtobufContentComponent, BranchAutocompleteComponent, CountryAutocompleteComponent, @@ -420,7 +421,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) EmbedImageDialogComponent, ImageGalleryDialogComponent, WidgetButtonComponent, - WidgetButtonToggleComponent, HexInputComponent, ScadaSymbolInputComponent ], @@ -642,6 +642,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) WidgetsBundleSearchComponent, CopyButtonComponent, TogglePasswordComponent, + WidgetButtonToggleComponent, ProtobufContentComponent, BranchAutocompleteComponent, CountryAutocompleteComponent, @@ -678,7 +679,6 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) EmbedImageDialogComponent, ImageGalleryDialogComponent, WidgetButtonComponent, - WidgetButtonToggleComponent, ScadaSymbolInputComponent ] }) 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 4619b14112..3b35fe55e8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5622,10 +5622,10 @@ "behavior": "Behavior", "on-click": "On click", "on-click-hint": "Action triggered when the button is clicked", - "left-button-click": "Left button click", - "left-button-click-hint": "Action while pressing on left button.", - "right-button-click": "Right button click", - "right-button-click-hint": "Action while pressing on right button.", + "first-button-click": "First button click", + "first-button-click-hint": "Action while pressing on first button.", + "second-button-click": "Second button click", + "second-button-click-hint": "Action while pressing on second button.", "button-click-hint": "Action while pressing on widget." }, "command-button": { @@ -5677,8 +5677,8 @@ "layout-rounded": "Rounded", "card-border": "Card border", "button-appearance": "Button appearance", - "left": "Left", - "right": "Right", + "first": "First", + "second": "Second", "color-styles": "Color styles", "selected": "Selected", "unselected": "Unselected" @@ -5710,16 +5710,16 @@ "activated-state-hint": "Configure condition under which the button is active.", "disabled-state": "Disabled state", "disabled-state-hint": "Configure condition under which the button is disabled.", - "initial-state": "Initial button", - "initial-state-hint": "Action to get the initial state of the component", + "selected-state": "Select button", + "selected-state-hint": "Configure condition under which the button is select.", "enabled": "Enabled", "hovered": "Hovered", "pressed": "Pressed", "activated": "Activated", "disabled": "Disabled", - "initial": "Left button", - "left": "Left", - "right": "Right" + "initial": "First button", + "first": "First", + "second": "Second" }, "background": { "background": "Background", From 8ab18b344e10f594dc4110e5d7c659c0d00e11f2 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 3 Dec 2024 13:49:31 +0200 Subject: [PATCH 009/103] UI: Refactoring shared import --- ui-ngx/src/app/shared/shared.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 424332ac45..7719dc683e 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -154,6 +154,7 @@ import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ import { MAT_DATE_LOCALE } from '@angular/material/core'; import { CopyButtonComponent } from '@shared/components/button/copy-button.component'; import { TogglePasswordComponent } from '@shared/components/button/toggle-password.component'; +import { WidgetButtonToggleComponent } from '@shared/components/button/widget-button-toggle.component'; import { HelpPopupComponent } from '@shared/components/help-popup.component'; import { TbPopoverComponent, TbPopoverDirective } from '@shared/components/popover.component'; import { TbStringTemplateOutletDirective } from '@shared/components/directives/sring-template-outlet.directive'; @@ -218,7 +219,6 @@ import { CountryAutocompleteComponent } from '@shared/components/country-autocom import { CountryData } from '@shared/models/country.models'; import { SvgXmlComponent } from '@shared/components/svg-xml.component'; import { DatapointsLimitComponent } from '@shared/components/time/datapoints-limit.component'; -import { WidgetButtonToggleComponent } from '@shared/components/button/widget-button-toggle.component'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; From b39f087feca4ae26503c7cd6d721a6e801332889 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 3 Dec 2024 13:53:45 +0200 Subject: [PATCH 010/103] UI: Rename segmented button to Two-segment button --- .../main/data/json/system/widget_types/two_segment_button.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_types/two_segment_button.json b/application/src/main/data/json/system/widget_types/two_segment_button.json index 96c7987e13..d32d139dce 100644 --- a/application/src/main/data/json/system/widget_types/two_segment_button.json +++ b/application/src/main/data/json/system/widget_types/two_segment_button.json @@ -1,6 +1,6 @@ { "fqn": "two_segment_button", - "name": "Two segment button", + "name": "Two-segment button", "deprecated": false, "image": "tb-image;/api/images/system/two-segment-button.svg", "description": "Facilitates 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 stat", From 8ba34a953ab20408f77674614018e279d1f43be4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 3 Dec 2024 14:06:06 +0200 Subject: [PATCH 011/103] Dashboard/widget resources export - process whole config --- .../controller/DashboardControllerTest.java | 20 +++- .../dao/resource/BaseResourceService.java | 108 ++++++++++++------ 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java index 88dd8f241c..ffbf95d990 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.controller; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -602,13 +603,14 @@ public class DashboardControllerTest extends AbstractControllerTest { dashboard.setTitle("My dashboard"); dashboard.setConfiguration(JacksonUtil.newObjectNode() .put("someImage", "tb-image;/api/images/tenant/" + imageInfo.getResourceKey()) - .set("widgets", JacksonUtil.toJsonNode(""" + .set("widgets", JacksonUtil.toJsonNode(""" {"xxx": {"config":{"actions":{"elementClick":[ {"customResources":[{"url":{"entityType":"TB_RESOURCE","id": "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"},"isModule":true}, {"url":"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js","isModule":true}]}]}}}} - """))); + """)) + .put("someResource", "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js")); dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); Dashboard exportedDashboard = doGet("/api/dashboard/" + dashboard.getUuidId() + "?includeResources=true", Dashboard.class); @@ -637,12 +639,18 @@ public class DashboardControllerTest extends AbstractControllerTest { doPost("/api/resource", resource, TbResourceInfo.class); Dashboard importedDashboard = doPost("/api/dashboard", exportedDashboard, Dashboard.class); + String newResourceKey = "gateway-management-extension_(1).js"; + imageRef = importedDashboard.getConfiguration().get("someImage").asText(); assertThat(imageRef).isEqualTo("tb-image;/api/images/tenant/" + imageInfo.getResourceKey()); - resourceRef = importedDashboard.getConfiguration().get("widgets").get("xxx").get("config") - .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText(); - String newResourceKey = "gateway-management-extension_(1).js"; - assertThat(resourceRef).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey); + + List resourcesRefs = new ArrayList<>(); + resourcesRefs.add(importedDashboard.getConfiguration().get("widgets").get("xxx").get("config") + .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText()); + resourcesRefs.add(importedDashboard.getConfiguration().get("someResource").asText()); + assertThat(resourcesRefs).allSatisfy(ref -> { + assertThat(ref).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey); + }); TbResourceInfo importedImageInfo = doGet("/api/images/tenant/" + imageInfo.getResourceKey() + "/info", TbResourceInfo.class); assertThat(importedImageInfo.getEtag()).isEqualTo(imageInfo.getEtag()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 22aa2ae748..7101922f6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -57,6 +57,7 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Base64; import java.util.Collection; import java.util.Collections; @@ -403,16 +404,26 @@ public class BaseResourceService extends AbstractCachedEntityService links = getResourcesLinks(dashboard.getResources()); - return updateResourcesUsage(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING, links); + return updateResourcesUsage(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @Override public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) { Map links = getResourcesLinks(widgetTypeDetails.getResources()); - boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING, links); + List jsonNodes = new ArrayList<>(2); + List> mappings = new ArrayList<>(2); + + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { - updated |= updateResourcesUsage(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING, links); + jsonNodes.add(defaultConfig); + mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); + } + + boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), jsonNodes, mappings, links); + if (defaultConfig != null) { widgetTypeDetails.setDefaultConfig(defaultConfig); } return updated; @@ -433,8 +444,9 @@ public class BaseResourceService extends AbstractCachedEntityService mapping, Map links) { - return processResources(jsonNode, mapping, value -> { + private boolean updateResourcesUsage(TenantId tenantId, List jsonNodes, List> mappings, Map links) { + log.trace("[{}] updateResourcesUsage (new links: {}) for {}", tenantId, links, jsonNodes); + return processResources(jsonNodes, mappings, value -> { String link = getResourceLink(value); if (link != null) { String newLink = links.get(link); @@ -463,22 +475,30 @@ public class BaseResourceService extends AbstractCachedEntityService getUsedResources(Dashboard dashboard) { - return getUsedResources(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING).values(); + return getUsedResources(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); } @Override public Collection getUsedResources(WidgetTypeDetails widgetTypeDetails) { - Map resources = getUsedResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING); + List jsonNodes = new ArrayList<>(2); + List> mappings = new ArrayList<>(2); + + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { - resources.putAll(getUsedResources(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING)); + jsonNodes.add(defaultConfig); + mappings.add(WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING); } - return resources.values(); + + return getUsedResources(widgetTypeDetails.getTenantId(), jsonNodes, mappings).values(); } - private Map getUsedResources(TenantId tenantId, JsonNode jsonNode, Map mapping) { + private Map getUsedResources(TenantId tenantId, List jsonNodes, List> mappings) { Map resources = new HashMap<>(); - processResources(jsonNode, mapping, value -> { + log.trace("[{}] getUsedResources for {}", tenantId, jsonNodes); + processResources(jsonNodes, mappings, value -> { String link = getResourceLink(value); if (link == null) { return value; @@ -517,32 +537,54 @@ public class BaseResourceService extends AbstractCachedEntityService mapping, UnaryOperator processor) { + private boolean processResources(List jsonNodes, List> mappings, UnaryOperator processor) { AtomicBoolean updated = new AtomicBoolean(false); - JacksonUtil.replaceByMapping(jsonNode, mapping, Collections.emptyMap(), (name, urlNode) -> { - String value = null; - if (urlNode.isTextual()) { // link is in the right place - value = urlNode.asText(); - } else { - JsonNode id = urlNode.get("id"); // old structure is used - if (id != null && id.isTextual()) { - value = id.asText(); + + for (int i = 0; i < jsonNodes.size(); i++) { + JsonNode jsonNode = jsonNodes.get(i); + // processing by mappings first + JacksonUtil.replaceByMapping(jsonNode, mappings.get(i), Collections.emptyMap(), (name, urlNode) -> { + String value = null; + if (urlNode.isTextual()) { // link is in the right place + value = urlNode.asText(); + } else { + JsonNode id = urlNode.get("id"); // old structure is used + if (id != null && id.isTextual()) { + value = id.asText(); + } } - } - if (StringUtils.isNotBlank(value)) { - value = processor.apply(value); - } else { - value = ""; - } + if (StringUtils.isNotBlank(value)) { + value = processor.apply(value); + } else { + value = ""; + } + + JsonNode newValue = new TextNode(value); + if (!newValue.toString().equals(urlNode.toString())) { + updated.set(true); + log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); + } + return newValue; + }); + + // processing all + JacksonUtil.replaceAll(jsonNode, "", (name, value) -> { + if (!StringUtils.startsWith(value, DataConstants.TB_RESOURCE_PREFIX + "/api/resource/")) { + return value; + } + + String newValue = processor.apply(value); + if (StringUtils.equals(value, newValue)) { + return value; + } else { + updated.set(true); + log.trace("Replaced '{}' ({}) with '{}'", value, name, newValue); + return newValue; + } + }); + } - JsonNode newValue = new TextNode(value); - if (!newValue.toString().equals(urlNode.toString())) { - updated.set(true); - log.trace("Replaced '{}' with '{}'", urlNode, newValue); - } - return newValue; - }); return updated.get(); } From 92c9453abef96a3003d3c5958bf3834fb360fff7 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 3 Dec 2024 16:05:03 +0200 Subject: [PATCH 012/103] Added show helps for bacnet connector basic config --- .../bacnet-device-name-field-expression_fn.md | 21 +++++++++++++++++++ ...acnet-device-profile-name-expression_fn.md | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-name-field-expression_fn.md create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-profile-name-expression_fn.md diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-name-field-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-name-field-expression_fn.md new file mode 100644 index 0000000000..3a97cc4376 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-name-field-expression_fn.md @@ -0,0 +1,21 @@ +## Expressions + +This field allows dynamically constructing a formatted device name using values extracted from a JSON object. You can specify variables to access the relevant fields in the JSON. + +# Available Variables + +You can use the following variables to extract specific device information: + +* `objectName`: Extracts the device's object name (e.g., `"Main Controller"`). +* `vendorId`: Extracts the device's vendor ID, typically a numeric identifier representing the manufacturer (e.g., `"1234"`). +* `objectId`: Extracts the device's unique object identifier (e.g., `"999"`). +* `address`: Extracts the device's network address (e.g., `"192.168.1.1"`). + +# Examples + +* `"Device ${objectName}"` + If the objectName variable exist and contains `"objectName": "Main Controller"`, the device on platform will have the following name: `Device Main Controller`. +* `"Vendor: ${vendorId}"` + If the vendorId variable exist and contains `"vendorId": 1234`, the device on platform will have the following name: `Vendor: 1234`. +* `"Device ID: ${objectId} at ${address}"` + If the objectId variable exist and contains `"vendorId": 999 `and address variable exist and contains `"address": "192.168.1.1"` , the device on platform will have the following name: `Device ID: 999 at 192.168.1.1`. diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-profile-name-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-profile-name-expression_fn.md new file mode 100644 index 0000000000..1b84c4d6ff --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/bacnet-device-profile-name-expression_fn.md @@ -0,0 +1,21 @@ +## Expressions + +This field allows dynamically constructing a formatted profile name using values extracted from a JSON object. You can specify variables to access the relevant fields in the JSON. + +# Available Variables + +You can use the following variables to extract specific device information: + +* `objectName`: Extracts the device's object name (e.g., `"Main Controller"`). +* `vendorId`: Extracts the device's vendor ID, typically a numeric identifier representing the manufacturer (e.g., `"1234"`). +* `objectId`: Extracts the device's unique object identifier (e.g., `"999"`). +* `address`: Extracts the device's network address (e.g., `"192.168.1.1"`). + +# Examples + +* `"Device ${objectName}"` + If the objectName variable exist and contains `"objectName": "Main Controller"`, the device on platform will have the following name: `Device Main Controller`. +* `"Vendor: ${vendorId}"` + If the vendorId variable exist and contains `"vendorId": 1234`, the device on platform will have the following name: `Vendor: 1234`. +* `"Device ID: ${objectId} at ${address}"` + If the objectId variable exist and contains `"vendorId": 999 `and address variable exist and contains `"address": "192.168.1.1"` , the device on platform will have the following name: `Device ID: 999 at 192.168.1.1`. From 78af38b3f3e4f01df8e6712d9df763fb1e8f56cd Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 3 Dec 2024 16:44:19 +0200 Subject: [PATCH 013/103] UI: Warning-Critical statte animation for hp scada --- .../src/main/data/json/system/scada_symbols/filter-hp.svg | 4 ++-- .../src/main/data/json/system/scada_symbols/heat-pump-hp.svg | 4 ++-- .../data/json/system/scada_symbols/horizontal-tank-hp.svg | 4 ++-- .../data/json/system/scada_symbols/horizontal-valve-hp.svg | 4 ++-- .../src/main/data/json/system/scada_symbols/pool-hp.svg | 4 ++-- .../src/main/data/json/system/scada_symbols/pump-hp.svg | 4 ++-- .../main/data/json/system/scada_symbols/sand-filter-hp.svg | 4 ++-- .../data/json/system/scada_symbols/short-vertical-tank-hp.svg | 4 ++-- .../main/data/json/system/scada_symbols/vertical-tank-hp.svg | 4 ++-- .../main/data/json/system/scada_symbols/vertical-valve-hp.svg | 4 ++-- .../general_high_performance_scada_symbols.json | 2 +- .../widget_bundles/high_performance_scada_fluid_system.json | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 +++- 13 files changed, 25 insertions(+), 23 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/filter-hp.svg b/application/src/main/data/json/system/scada_symbols/filter-hp.svg index 02328a62ef..6225d70b0e 100644 --- a/application/src/main/data/json/system/scada_symbols/filter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/filter-hp.svg @@ -163,8 +163,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg index 003c30df02..1d7babc3b0 100644 --- a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg @@ -306,8 +306,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg index 9ee3ebda66..cff6a97eb9 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg @@ -247,8 +247,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg index 2e542cecae..754bd24a4f 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg @@ -272,8 +272,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/pool-hp.svg b/application/src/main/data/json/system/scada_symbols/pool-hp.svg index e67280d617..357563a687 100644 --- a/application/src/main/data/json/system/scada_symbols/pool-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/pool-hp.svg @@ -247,8 +247,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/pump-hp.svg b/application/src/main/data/json/system/scada_symbols/pump-hp.svg index daaca4ae9c..422a8b5259 100644 --- a/application/src/main/data/json/system/scada_symbols/pump-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/pump-hp.svg @@ -206,8 +206,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg b/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg index 01cc98ce0f..773837608e 100644 --- a/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg @@ -279,8 +279,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg index 8a86100091..1f635fc810 100644 --- a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg @@ -247,8 +247,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg index 4a7bbb3fdf..3e29a4e657 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg @@ -247,8 +247,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg index 200e93cb95..6c2683c123 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg @@ -272,8 +272,8 @@ }, { "id": "criticalAnimation", - "name": "{i18n:scada.symbol.critical-state-animation}", - "hint": "{i18n:scada.symbol.critical-state-animation-hint}", + "name": "{i18n:scada.symbol.warning-critical-state-animation}", + "hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}", "group": null, "type": "value", "valueType": "BOOLEAN", diff --git a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json index dd5ad38e12..ffb0fee835 100644 --- a/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json +++ b/application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json @@ -5,7 +5,7 @@ "image": "tb-image:aHBfc2NhZGFfZ2VuZXJhbF9zeW1ib2xzX3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkdlbmVyYWwgaGlnaC1wZXJmb3JtYW5jZSBTQ0FEQSBzdW1ib2xzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEXe3t7f39/f39/X19cAAADf39/e3t7d3d3e3t6VlZXI3/eZmZlmZmbGxsbr6+vc3Nytra3Y2Nikrri3xtZ9fX0aGho1NTW5ubnS0tKUlJS/v7+urq6zs7Pa2tqJiYm0tLR5eXnPz8/b29t8fHyhoaJxcXImJia+vr5WVlZjY2NNTU0wMDCpqamzx9zCx9GxsbGEhIRZWVlubm7Q09jJycmLmbhvg6tBQUG3ydvR0dEbPoW90+qzu8spSosNM3+nsMWZpb6TlpkAKHhhZWpfX19UVFRGRkbU1NR9jrKmpqZTbJ43VZFdZm5qampFYZh4foRgw9f8AAAACHRSTlPvvyAgAK+wr43PeosAAAVgSURBVHja7d1pV9pAGIZhuj9TMw1t02RiZawkYYlKRSm21qWb3Zf//286Y8RCgBgIlBecWwkh+sHrvIF45hyldPfOrRKWvXt37pbuLD9DpRgPsBKtwGmVVFoVCAyEWjcWsreftAdiTQp5YSU5IJaBgFgGAmIZCIhlICCWgWDSBJ9hYnEQyWbboiD8OWZayNFruSFlAzEQopCd9aSdohAh9NHFQbYeJW0UhVgMgIv+IrHMEMFCACHjCGtSS8pltS9CIBTcC0OhH+odshDJOXfBY2ZJRFHoiucJxI3kof4KXM4OWVRzpAu9E1OFxJZluXAc7ikOLx8+h8uRTEnKS4iFqx24gigkObWstuNIRBZz+yDMGQGJOWmIjACOGvBvIgIWEy5Q64O01Q7VUyuBIIpjCcc9jEPImOtj8WF8cbh/Iq7lSqKQsbkYjll0X35XH7JsF0QDoQKxbegPfX+5kxzVx5cI8uXbWkbfHkMlvPWMNr9QgNgv1zLzbAAeMvNmA3nyOunJNBDb3smGbEBJ1pHZ5mwgTx8mTQXBtRDbtpcAYg9ORH7+LFOQl2mI/8tH53zTiShB7BRkW0rX3/8kMyAbEZwd/1f9fJMYpNoPqalPue1sZ0BkiB3Hj+rn9XlAdn++nQqCFGRbQz4rTx+kqiB9P7TvY0duSHzCHCC778XHt7OYSCOOG2mIPQgRn/zzzpwgu+8BJZkKUlWQvl44a75cczImgo66/okO6nOA/IGK/yx4aqVLQ9IRm0gOSHX0dYTSc8SGgpSvn4jt/a8L4m70duqX33o2ZN1WdbaQUaez8OsIFKTCvfHVOxUNwdambuNis6Fv+rNXx6YBqTzOqFK1dehlA7Yq2VxFAZIlqVQqVQUBbPTXUxCDXEiuMMlepefoDcQecCRbOpAQl5JqtaI0A1Wr6qbSCsDGUIQmAnaVPz6WO4lFQZLs60LeFgyBrS1pD3r3SwS5tOhN8uDKhjyRggxVCEEKck0GMj4DKZSBjM9ACmUg41tRyMFZCxe1sPjyQR4jaQDSPf1+1AXw9fTkAAsvH8QPkdQHaZ220PpwgFdHFAaSE7JnRS+h6occvAOgID9eteYh4Zik/BDL2tsahOi634HTk5OjLmZesO0jf/khOn8rBXmnz6oP73BweoxZFzx7lp8yIcSKvAHI8UkLCtKCOr1QrDfldM1nzzSlPBRHYcjeOvohrZNjQCHOgKOzYoyoIVmqtobUApZONpuiGGT/ZerJfvS72+2eqfPr6/cTFKrpY6hAM95gRKxZBLIfPkYK8kp3DBx3vx6gSLyJERDFGOfm00PkFpCCzK6QYThvHANgDKlyQxRjjhDGCn8/jV8aDURlIAZiIAYyPwhjBmIgBmIgBmIgU0I4Y80mY2LpIaJ2sSzCMZwMMFFNb5EQBBoSYETbNYEJ8hpYKETUxgzEV778611h0OazgBzvJoWYuGDcQNTCj8i93sXKwCwg6yLpG6YYybiBjBGi6SNnRf4QZvKCaMxAkpFkr3dRgjCG4XgQNBpB4F273kUdknXcQAzEQAzEQAzEQAzkhkG8etIarlpOyIj/r2UgNwMytHy0rJCh5SNCEC/KXKfKXj6iBEHDG+lr5Fk+IgXhjWbWOlX28hElCFBmQ5VzLx9RgkxUECFvtCFkryOXGUhGBmIgKgPJ6CZDGkkBpo4GhGw3FSJCDsBjZVBrQojF2oBz6FiuAK0mhLTDNrhGtCVoNSGEMw7Z1jsctCph4hxyb9kxLSQCwaaAhDEASW0sU0DgSlF2V+A5Am7F7ecg1k29stPNQKhlINRamTfWLd3CSnS/dHslRqIYd2/fw7JXunX77l/CoXsjeoRJ3gAAAABJRU5ErkJggg==", "scada": true, "description": "Bundle with high-performance SCADA symbols", - "order": 9500, + "order": 9400, "name": "General high-performance SCADA symbols" }, "widgetTypeFqns": [ diff --git a/application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json b/application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json index c30667e6de..1e41b22446 100644 --- a/application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json +++ b/application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json @@ -5,7 +5,7 @@ "image": "tb-image:aHBfc2NhZGFfZmx1aWRfc3lzdGVtX2J1bmRsZV9pbWFnZS5wbmc=:IkhpZ2gtcGVyZm9ybWFuY2UgU0NBREEgZmx1aWQgc3lzdGVtIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABNVBMVEXe3t7f39/X19cAAADf39/e3t7f39/e3t7e3t7////I3/fr6+vHx8esrKzj4+OcrsHT0tKVlZWvr6/Kysrx8fF0dHT9/f24uLjBwcHc29v39/etra27u7ubm5vt7e2/v7/Dw8OysrKqqqq+1ev6+vrE2/Lh4eHV1dWoqKiQkJCmpqa1yt+2trbu7e2YmJi/1eyurq7B2O9ycXFlZWXT09Ojo6PMzMzIyMjm5eWBgYGenp78/Py0tLSTk5OLi4uwxdq6z+Wous6ioqKGhoZjY2OgoKBoaGheXl5WVlasv9SorrR/fn7R0dGlpaVSUlKhrr13d3evuseTpLWWoq+fn59sbGw+Pj6rv9Tv8veitci4uryAk7vf5O7P1+WQocRgeasvLy8hISG/yd2frsx/k7tvhrNAXZmZOz+XAAAACHRSTlPvICAAv7Cvv9cI9yEAAAzySURBVHja7JttT9pQGIaNe8lTejg9dDRqJEElwFxNN1lIZNko3RrRuoAvUaOf/P+/YkecPq0v0PY5lZZ48dUvV06vm54Ylt4tv1+CovNx+d3ScvE1JFLjAywE8R6rNcg9SzNEama36/R/raxLVmazsfrTrMFcmCYiNrdAb7VX6ht6u91e0+MhdkwGBNSLdDd0ZK/V0mOzYwIBxSLiJ+hhEc71+EBdAAGVIuKzTuIzwUSpyKoeZa/FE5oAAXUijo609D0UiQ+hE3UiTOhIvf5Jb0kSimwIoEIX2dTDIp9wfpOwCXGpHHyfwqiSXgSHNzy/mR1J5RtMYT29SE1HEseOmHMX+aIroT53kdVnToTvJRbZmbtINJE1+bl9tAooYpTCGIZbakhKSTFyJnLlHZZs2x4UXmRC8+vXRRHZXQwRyWKILMCJNO2GvRAiJds7VDy/XZqImVLEHaie35s/LCrSX59CPyrChhe5mV+LW1ZYhVUi+H4lAgNEHPWqVm5itzSN+87LybwcRd9nmmblZn4tTbJ1YCYVMc9XNEmuTuSW7WOWRIQNt6saRWTQcKXIblO5iMatIzZbBOPQNJLIlYHzq1REwg+ceCLObRxEEdt2cX7VikxSqc0WMc+3NMTKz/xqYbb32XQRti/j+E8uY9cwlSki4qjDtRC5m98wbUzlsYjTmcRRiBO5S4U9J2IOMQ6yiDvwshfRqg+poAg7xjhUiGQ4v09TQRFx5GMcKkQuB3amIgg7cFCkG42jAPMbSWXI7kTM4Q/tnkLFfg+XqVQq4tE3R2HmNwzvXYwmcRT7RKSI3+v1MhFxB/YriOAKi8lLCVcu0jyV8zuwbfs1RH4MzfvLR1n5iTQbrza/nS7+kTPKZn53s3u0MA4BYXoWL2DsVbz4Rt7fiza/ZRnHM7BRuVAnwjoOPAJTaSubX9vNVoR3MI6nCEyFKpLp/OL7O/I0FTUijUGG81s+N2EmtVE5540w34FYOCOeY5FqD+OYBZOp5HR+q9tDBnHAi28uT6SGccTFHJVJV91LT70I8/uQAsfn6UUOPeXzi3EkhVkWTytiN1XP7wXGkRz25+a1G/HgBUwg0Y0tMi49It3/R04gW2aLnJSUcAY06CLf1Dxa10CDLiK8yPy6V6lEPCBCF4Egslqul2p+AyBCF4keSVN+Usyvy4AKXQQCeiMVoEMXgbOnIjne3iki4ow4vX+BAF0EEScYRTPpF2JjTPWgiyDB+OHSnvDR8n4DGboIwoKxO9nfRPPbOAsIx6FWBLkOxieeYRinEmM243EQXMNcWIgfuL6J5JE3kX/tneeu0zAUgEEsH8mx3MY0o0naRIiEqK0Q6oDS3h8gJCRA4gcgJJaY7/8I2Bk9GaVJKKOMD6Gbyy3H/myfeKSFU+O/yKnxj4nw/uzBIjY0yY07Dx9eXWuHwHOn1cigPwt/3lWEznyNQRFhrNchfBONZMyn8BOZrjqJPPEp7CNcU9Ek4sNPZdRehJ+5Ar4J1dhhEQo/Fae1iB8LOAilv1OEthSh9xk0wW6wkxd5EkEbAu3ERbCpG2BrccIifCugNWvWKEL38QtE+Ay6ELAGkRXsQYQ/UQQ9jjCpi9D9FfnpIlvoSiBOUWTfJCiCReDOegs36E8Y1Fn/aBERjkeSMRXfLfKE1YJGwU1Odty8Y9Zf4vxIEToynbxAZ9MPv0uEbqp1dM84qdA7q6pQ2kbk7ds3r941icwnlfKk13eIONWXu0nYobudbYLI37oPku/9auy1aBZ5+eHdS3h1WOR6psEN3/dHw8wp9rqKzBiUmDkq6MRl4N2GBBb0VAh6j1WyqFkE3nyCj68PivRp0prBZumAIlxqwySW3lEkKtfuPpcaQVrkLpTJHBXELZsYorlHvrx6/flQjogg0RipUHwKKSvfT2rGuoj4rBT3hgqbu9lzSOEAgVs3oUcnO1NRaV8ANlz6NaZqeLEOIuVM36raziHHyr6ogKxuEoruInUPf1eeZUOCqX4UYys3idQzJOayro91LMgqtJTo8erUuT5SZKIG0AQkaCDh6RSgfijaiiygQECTNvfQhKfxsb94/ANF/KSqnlkRsTzYmTgtRSgtzR+E9JjKDRUQ+2JqFTLoJgNE0GNEGE3TAE0sq9huIiaE83YipfaN08kxM8HGMSBjeuMmIfehgEYVd2oiId2xWtEcBiVcQugU6g3HIWOywho2iCwAYT4hdwBKJrYuf1tYTF92CS3VpQSFDpgqm3fFoYju5e3G1bGJ2UaEG+XAONI8fRcZPTwQi3K+U7pfhF2XvyTXQwbXc+ZiUDnc8fcUhwPATAZJ3EakzwCRCXADv7PSaX2FGTJnAKpLFgKQJ3tFBtg188G4cA1ISIutfZtnItgh6mKCWXJQxAWE3ZTfr1Y611M7007Hq14qit4kfALIep/IoCBy/S6KjAdooppkONRNa5pVe5rlZC5nKiHhELJqIfKsfE9/wLIq65ZKjzS+hR5ZVtwDZNbUI3URHFkaqAzUzUTCSEXyDsk2DiuVx80ii/Kk7gKOWWt6ezq7f3b/eRpQqqXcI+SsqL+3R+4mF04ick2KCMaYkCJ3YYD7GRxZti7DexaEdNTPytFZ5k4IZ00imEjY1IhlPTWXkhtMleQVat4DRPu2yNX3N3IR9uL9C6ZEsEsoITzEOJ6hjx2RnwbYppf7qkxqFgkAmWVeaGYuN+tgubwDlr4rzzyjZMhgh0P3iVyTF/Tq1athJgLsIVPNe21QHFlDKOKNpICdDDEsz7YiQkbNIhogvcwLRZb9W7duPVk+mHhZUH1uAXMIDWv333qP5CiRDCUyqN98cQVq3LolNYYGyy24afuEOEeKnC23UuTRcnJPl8xlSmY3N0qbRK7tEykPLT1taYTC9JaEwcaTWCtT1201lI8SsT2+4jeWaxl4uwywA9qK3G0WCasiI/CUiAWxxbk1tUHSWsQAZJFuFr2VPuf6bds2by2Xs0f3zWW2uMKaDxuH1mBOM5zJtcfjnMe1oZXdf2V36w8A4lu3eri07CISlyd2Vw6hbHNzW7fZ2VKhjaGIIacb0XTXGly7Zmdcu3b32u5yUBLpA9N1k6czoe456eqXQolRq2SfAfIA76vZ7ZY9DZabO2fGFMod5wISVETQJOPu3bvXdqCHmiDo0MrLM+QVc0DiLKGIuCnDNosMAYlwFTU1IcHUYip0sOa8tCZ5AIj/DZHB3UGKvM5c7qo/Km1GJvnqx0yL1YaU+oybhZabq+mmWSQqD35OS6sR3bO8dKniWTqfZrmuykee1UWQpNpKJQcK4H1Vz8ozwEzXvNzUp7gic9osUQQgZ8mmCT2mLN1XWdM8J7m+imQ3sqa1VkVnv8hKVlFkq+p8tWvmu0RbupiWZytdret+xE/3sVMd8gYCj+3WwbiLXAMSOs0iWbdABc4J2aBHUu4K18GS2xbv4c7qoAjxK+v4e+iRDSsF6m4ooQEgwRE7xE1y17QK7QYcKg3nYhUPi7hQ4J468tMho3AOYJvl3TDy7Mit7mbXbrqHxfV3L+Etd4ikTwFhMrO2Aj0kenknuiblDmGLI0TUMHUmUOgQmNupiYHt5rM2IpWF24bjIYkBeWQ0iWllqT8mx4hwjidwlpcYlBtObXRNaCfiisqJKe9hYDxBkaGVR/XM9P5RIuAQvAUXv2RHXSFGbBbhQfUslm9FIaKBk/2aVk4rgNLjRESc/51pLmLnJoa9wmPsBpF6uuNJNa+cKwNTzxucMexN9e4imAQkYjiEbX0nqWao1RRai/CgbnLzbOSBAg+wXRV3MYEiGj1GBE10E0wsLuXxSHlc7/TESpRD3+BE/bNEIaRwO3uoiM8bcL18tAiwmEjcZ3hSgA8V/Wm3Z4g+lIkfEInj3otoyBjdLtybSSW3AkpE9FgRhUh3/X6fpuFNMR/FPBkqrOvD0GrxTMVWcOfm7jKovWpGjhFBTJ8k3BxNJuNJ5PDsIXX35+xxTT281+ME4cMtBQQf0h0tgioIPlTsKoIbDEQY99wHQ2M8HvbkGNsT1SXHiyCm5nCSwZ1gggV2EuEx7IWFNGSwl4g2iHSGjkYOpY4zDo94Uw2NoAN1j9N54xmNunqcqAihbjePkxUhfMugHWJGyQmLEOI2VQOn/tMWIdQV0EjkEHLqIqQXOE2h1i4/EGADP5XHLUVcIceNBg3vjd8eEIkF/ESE3yyC7whkgcH2h9Eo2y30u+fZ8cw3pJUIjbDhjWp9Qk2jIr/uH/wETQPumO7FeNJvgLf93/dKFTccQ9ZdoRmBpoVQYEPJd4J7pTrXV6SBBpF4lBBpddY3JFqdaJRwg3QEPZpNuoss4HsZk248mUwieoBgMknT+dRFaNvP7PwFIph93UUi7Xs5I92IaCMRac25v+UTlecukr+CS+fO/xVdIjUunL9M/nTOXTl/4St0s1h4GZ5DIgAAAABJRU5ErkJggg==", "scada": true, "description": "Bundle with high-performance SCADA symbols for fluid system", - "order": 9600, + "order": 9350, "name": "High-performance SCADA fluid system" }, "widgetTypeFqns": [ 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 3ab8283064..c2f45da84f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3179,8 +3179,10 @@ "critical": "Critical", "critical-click": "Critical click", "critical-state-hint": "Indicates whether component is in critical state.", - "critical-state-animation": "Alarm state animation", + "critical-state-animation": "Critical state animation", "critical-state-animation-hint": "Whether to enable blinking animation when component is in critical state.", + "warning-critical-state-animation": "Warning/Critical state animation", + "warning-critical-state-animation-hint": "Whether to enable blinking animation when component is in warning or critical state.", "animation": "Animation", "broken": "Broken", "broken-hint": "Indicates whether component is broken.", From a0dcd7cf3058d7fe534987c062a78ab63f8afb47 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 3 Dec 2024 17:07:45 +0200 Subject: [PATCH 014/103] UI: Add JS modules support to map widgets JS functions. --- ui-ngx/src/app/core/utils.ts | 15 -- .../home/components/widget/lib/maps/circle.ts | 11 +- .../widget/lib/maps/common-maps-utils.ts | 5 +- .../components/widget/lib/maps/leaflet-map.ts | 10 +- .../components/widget/lib/maps/map-models.ts | 78 +++++----- .../components/widget/lib/maps/map-widget2.ts | 142 +++++++++++------- .../components/widget/lib/maps/maps-utils.ts | 12 +- .../components/widget/lib/maps/markers.ts | 16 +- .../components/widget/lib/maps/polygon.ts | 11 +- .../widget/lib/maps/providers/image-map.ts | 22 ++- .../map/circle-settings.component.html | 4 + .../marker-clustering-settings.component.html | 1 + .../map/markers-settings.component.html | 5 + .../map/polygon-settings.component.html | 4 + ...p-animation-common-settings.component.html | 1 + ...p-animation-marker-settings.component.html | 2 + ...rip-animation-path-settings.component.html | 1 + ...ip-animation-point-settings.component.html | 2 + .../trip-animation.component.ts | 123 ++++++++++----- 19 files changed, 279 insertions(+), 186 deletions(-) diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index b67f41d0cf..fed2de125f 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -711,21 +711,6 @@ export function safeExecuteTbFunction(func: CompiledT return res; } - -export function safeExecute(func: (...args: any[]) => any, params = []) { - let res = null; - if (func && typeof (func) === 'function') { - try { - res = func(...params); - } - catch (err) { - console.log('error in external function:', err); - res = null; - } - } - return res; -} - export function padValue(val: any, dec: number): string { let strVal; let n; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts index 225dc66e06..680f3faca6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/circle.ts @@ -16,14 +16,11 @@ import L, { LeafletMouseEvent } from 'leaflet'; import { CircleData, WidgetCircleSettings } from '@home/components/widget/lib/maps/map-models'; -import { - functionValueCalculator, - parseWithTranslation -} from '@home/components/widget/lib/maps/common-maps-utils'; +import { functionValueCalculator, parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; import { createTooltip } from '@home/components/widget/lib/maps/maps-utils'; import { FormattedData } from '@shared/models/widget.models'; -import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; +import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils'; export class Circle { @@ -94,7 +91,7 @@ export class Circle { if (this.settings.showCircleLabel) { if (!this.map.circleLabelText || this.settings.useCircleLabelFunction) { const pattern = this.settings.useCircleLabelFunction ? - safeExecute(this.settings.parsedCircleLabelFunction, + safeExecuteTbFunction(this.settings.parsedCircleLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleLabel; this.map.circleLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.replaceInfoTooltipCircle = processDataPattern(this.map.circleLabelText, this.data); @@ -109,7 +106,7 @@ export class Circle { private updateTooltip() { const pattern = this.settings.useCircleTooltipFunction ? - safeExecute(this.settings.parsedCircleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : + safeExecuteTbFunction(this.settings.parsedCircleTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.circleTooltipPattern; this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, this.data, true)); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts index 25f7b1ceec..cd974ee22c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts @@ -29,6 +29,7 @@ import { map } from 'rxjs/operators'; import { FormattedData } from '@shared/models/widget.models'; import L from 'leaflet'; import { ImagePipe } from '@shared/pipe/image.pipe'; +import { CompiledTbFunction, GenericFunction } from '@shared/models/js-function.models'; export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number { return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); @@ -257,11 +258,11 @@ export const parseWithTranslation = { } }; -export function functionValueCalculator(useFunction: boolean, func: (...args: any[]) => any, params = [], defaultValue: T): T { +export function functionValueCalculator(useFunction: boolean, func: CompiledTbFunction, params = [], defaultValue: T): T { let res: T; if (useFunction && isDefined(func) && isFunction(func)) { try { - res = func(...params); + res = func.execute(...params); if (!isDefinedAndNotNull(res) || res === '') { res = defaultValue; } 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 29b2768e58..ee5af0bfb5 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 @@ -54,7 +54,7 @@ import { isNotEmptyStr, isString, mergeFormattedData, - safeExecute + safeExecuteTbFunction } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { @@ -63,9 +63,9 @@ import { } from '@home/components/widget/lib/maps/dialogs/select-entity-dialog.component'; 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'; +import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; export default abstract class LeafletMap { @@ -149,7 +149,7 @@ export default abstract class LeafletMap { const childCount = cluster.getChildCount(); const formattedData = cluster.getAllChildMarkers().map(clusterMarker => clusterMarker.options.tbMarkerData); const markerColor = markerClusteringSettings.clusterMarkerFunction - ? safeExecute(markerClusteringSettings.parsedClusterMarkerFunction, + ? safeExecuteTbFunction(markerClusteringSettings.parsedClusterMarkerFunction, [formattedData, childCount]) : null; if (isDefinedAndNotNull(markerColor) && tinycolor(markerColor).isValid()) { @@ -899,7 +899,7 @@ export default abstract class LeafletMap { rawMarkers.forEach(data => { if (data.rotationAngle || data.rotationAngle === 0) { const currentImage: MarkerImageInfo = this.options.useMarkerImageFunction ? - safeExecute(this.options.parsedMarkerImageFunction, + safeExecuteTbFunction(this.options.parsedMarkerImageFunction, [data, this.options.markerImages, markersData, data.dsIndex]) : this.options.currentImage; const imageUrl$ = currentImage @@ -1042,7 +1042,7 @@ export default abstract class LeafletMap { if (!!this.extractPosition(pdata)) { const dsData = pointsData.map(ds => ds[tsIndex]); if (this.options.useColorPointFunction) { - pointColor = safeExecute(this.options.parsedColorPointFunction, [pdata, dsData, pdata.dsIndex]); + pointColor = safeExecuteTbFunction(this.options.parsedColorPointFunction, [pdata, dsData, pdata.dsIndex]); } const point = L.circleMarker(this.convertPosition(pdata, dsData), { color: pointColor, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts index af8f51ffdd..6705c7b853 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-models.ts @@ -18,6 +18,7 @@ import { Datasource, FormattedData } from '@app/shared/models/widget.models'; import tinycolor from 'tinycolor2'; import { BaseIconOptions, Icon } from 'leaflet'; import { Observable } from 'rxjs'; +import { CompiledTbFunction, TbFunction } from '@shared/models/js-function.models'; export const DEFAULT_MAP_PAGE_SIZE = 16384; export const DEFAULT_ZOOM_LEVEL = 8; @@ -273,7 +274,7 @@ export interface TripAnimationCommonSettings { } export interface WidgetTripAnimationCommonSettings extends TripAnimationCommonSettings { - parsedTooltipFunction: GenericFunction; + parsedTooltipFunction: CompiledTbFunction; } export const defaultTripAnimationCommonSettings: TripAnimationCommonSettings = { @@ -305,35 +306,35 @@ export const showTooltipActionTranslationMap = new Map; + parsedTooltipFunction: CompiledTbFunction; + parsedColorFunction: CompiledTbFunction; + parsedMarkerImageFunction: CompiledTbFunction; markerClick: { [name: string]: actionsHandler }; currentImage: MarkerImageInfo; tinyColor: tinycolor.Instance; @@ -382,8 +383,7 @@ export interface TripAnimationMarkerSettings { } export interface WidgetTripAnimationMarkerSettings extends TripAnimationMarkerSettings { - parsedLabelFunction: GenericFunction; - parsedMarkerImageFunction: MarkerImageFunction; + parsedLabelFunction: CompiledTbFunction; } export const defaultTripAnimationMarkersSettings: TripAnimationMarkerSettings = { @@ -406,29 +406,29 @@ export interface PolygonSettings { showPolygonLabel: boolean; usePolygonLabelFunction: boolean; polygonLabel?: string; - polygonLabelFunction?: string; + polygonLabelFunction?: TbFunction; showPolygonTooltip: boolean; showPolygonTooltipAction: ShowTooltipAction; autoClosePolygonTooltip: boolean; usePolygonTooltipFunction: boolean; polygonTooltipPattern?: string; - polygonTooltipFunction?: string; + polygonTooltipFunction?: TbFunction; polygonColor?: string; polygonOpacity?: number; usePolygonColorFunction: boolean; - polygonColorFunction?: string; + polygonColorFunction?: TbFunction; polygonStrokeColor?: string; polygonStrokeOpacity?: number; polygonStrokeWeight?: number; usePolygonStrokeColorFunction: boolean; - polygonStrokeColorFunction?: string; + polygonStrokeColorFunction?: TbFunction; } export interface WidgetPolygonSettings extends PolygonSettings, WidgetToolipSettings { - parsedPolygonLabelFunction: GenericFunction; - parsedPolygonTooltipFunction: GenericFunction; - parsedPolygonColorFunction: GenericFunction; - parsedPolygonStrokeColorFunction: GenericFunction; + parsedPolygonLabelFunction: CompiledTbFunction; + parsedPolygonTooltipFunction: CompiledTbFunction; + parsedPolygonColorFunction: CompiledTbFunction; + parsedPolygonStrokeColorFunction: CompiledTbFunction; polygonClick: { [name: string]: actionsHandler }; } @@ -464,29 +464,29 @@ export interface CircleSettings { showCircleLabel: boolean; useCircleLabelFunction: boolean; circleLabel?: string; - circleLabelFunction?: string; + circleLabelFunction?: TbFunction; showCircleTooltip: boolean; showCircleTooltipAction: ShowTooltipAction; autoCloseCircleTooltip: boolean; useCircleTooltipFunction: boolean; circleTooltipPattern?: string; - circleTooltipFunction?: string; + circleTooltipFunction?: TbFunction; circleFillColor?: string; circleFillColorOpacity?: number; useCircleFillColorFunction: boolean; - circleFillColorFunction?: string; + circleFillColorFunction?: TbFunction; circleStrokeColor?: string; circleStrokeOpacity?: number; circleStrokeWeight?: number; useCircleStrokeColorFunction: boolean; - circleStrokeColorFunction?: string; + circleStrokeColorFunction?: TbFunction; } export interface WidgetCircleSettings extends CircleSettings, WidgetToolipSettings { - parsedCircleLabelFunction: GenericFunction; - parsedCircleTooltipFunction: GenericFunction; - parsedCircleFillColorFunction: GenericFunction; - parsedCircleStrokeColorFunction: GenericFunction; + parsedCircleLabelFunction: CompiledTbFunction; + parsedCircleTooltipFunction: CompiledTbFunction; + parsedCircleFillColorFunction: CompiledTbFunction; + parsedCircleStrokeColorFunction: CompiledTbFunction; circleClick: { [name: string]: actionsHandler }; } @@ -530,13 +530,13 @@ export const polylineDecoratorSymbolTranslationMap = new Map; + parsedStrokeOpacityFunction: CompiledTbFunction; + parsedStrokeWeightFunction: CompiledTbFunction; } export const defaultRouteMapSettings: PolylineSettings = { @@ -578,7 +578,7 @@ export interface PointsSettings { showPoints?: boolean; pointColor?: string; useColorPointFunction?: false; - colorPointFunction?: string; + colorPointFunction?: TbFunction; pointSize?: number; usePointAsAnchor?: false; pointAsAnchorFunction?: string; @@ -586,8 +586,8 @@ export interface PointsSettings { } export interface WidgetPointsSettings extends PointsSettings { - parsedColorPointFunction: GenericFunction; - parsedPointAsAnchorFunction: GenericFunction; + parsedColorPointFunction: CompiledTbFunction; + parsedPointAsAnchorFunction: CompiledTbFunction; } export const defaultTripAnimationPointSettings: PointsSettings = { @@ -612,11 +612,11 @@ export interface MarkerClusteringSettings { chunkedLoading: boolean; removeOutsideVisibleBounds: boolean; useIconCreateFunction: boolean; - clusterMarkerFunction?: string; + clusterMarkerFunction?: TbFunction; } export interface WidgetMarkerClusteringSettings extends MarkerClusteringSettings { - parsedClusterMarkerFunction?: GenericFunction; + parsedClusterMarkerFunction?: CompiledTbFunction; } export const defaultMarkerClusteringSettings: MarkerClusteringSettings = { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index 58702653e5..8dea835e24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -30,9 +30,9 @@ import { TranslateService } from '@ngx-translate/core'; import { UtilsService } from '@core/services/utils.service'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { providerClass } from '@home/components/widget/lib/maps/providers/public-api'; -import { isDefined, isDefinedAndNotNull, parseFunction } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, parseFunction, parseTbFunction } from '@core/utils'; import L from 'leaflet'; -import { forkJoin, Observable, of } from 'rxjs'; +import { firstValueFrom, forkJoin, from, Observable, of } from 'rxjs'; import { AttributeService } from '@core/http/attribute.service'; import { EntityId } from '@shared/models/id/entity-id'; import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; @@ -40,12 +40,18 @@ import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/tel // @dynamic export class MapWidgetController implements MapWidgetInterface { + private updatePending = false; + private latestUpdatePending = false; + private resizePending = false; + private destroyed = false; + constructor( public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement, - isEdit?: boolean + isEdit = false, + mapLoaded?: (map: LeafletMap) => void ) { if (this.map) { this.map.map.remove(); @@ -56,37 +62,53 @@ export class MapWidgetController implements MapWidgetInterface { if (!$element) { $element = ctx.$container[0]; } - this.settings = this.initSettings(ctx.settings, isEdit); - this.settings.tooltipAction = this.getDescriptors('tooltipAction'); - this.settings.markerClick = this.getDescriptors('markerClick'); - this.settings.polygonClick = this.getDescriptors('polygonClick'); - this.settings.circleClick = this.getDescriptors('circleClick'); + from(this.initSettings(ctx.settings, isEdit)).subscribe(settings => { + if (!this.destroyed) { + this.settings = settings; + this.settings.tooltipAction = this.getDescriptors('tooltipAction'); + this.settings.markerClick = this.getDescriptors('markerClick'); + this.settings.polygonClick = this.getDescriptors('polygonClick'); + this.settings.circleClick = this.getDescriptors('circleClick'); - const MapClass = providerClass[this.provider]; - if (!MapClass) { - return; - } - parseWithTranslation.setTranslate(this.translate); - this.map = new MapClass(this.ctx, $element, this.settings); - (this.ctx as any).mapInstance = this.map; - this.map.saveMarkerLocation = this.setMarkerLocation.bind(this); - this.map.savePolygonLocation = this.savePolygonLocation.bind(this); - this.map.saveLocation = this.saveLocation.bind(this); - let pageSize = this.settings.mapPageSize; - if (isDefinedAndNotNull(this.ctx.widgetConfig.pageSize)) { - pageSize = Math.max(pageSize, this.ctx.widgetConfig.pageSize); - } - this.pageLink = { - page: 0, - pageSize, - textSearch: null, - dynamic: true - }; - this.map.setLoading(true); - this.ctx.defaultSubscription.paginatedDataSubscriptionUpdated.subscribe(() => { - this.map.resetState(); + const MapClass = providerClass[this.provider]; + if (!MapClass) { + return; + } + parseWithTranslation.setTranslate(this.translate); + this.map = new MapClass(this.ctx, $element, this.settings); + (this.ctx as any).mapInstance = this.map; + this.map.saveMarkerLocation = this.setMarkerLocation.bind(this); + this.map.savePolygonLocation = this.savePolygonLocation.bind(this); + this.map.saveLocation = this.saveLocation.bind(this); + let pageSize = this.settings.mapPageSize; + if (isDefinedAndNotNull(this.ctx.widgetConfig.pageSize)) { + pageSize = Math.max(pageSize, this.ctx.widgetConfig.pageSize); + } + this.pageLink = { + page: 0, + pageSize, + textSearch: null, + dynamic: true + }; + this.map.setLoading(true); + this.ctx.defaultSubscription.paginatedDataSubscriptionUpdated.subscribe(() => { + this.map.resetState(); + }); + this.ctx.defaultSubscription.subscribeAllForPaginatedData(this.pageLink, null); + if (this.updatePending) { + this.update(); + } + if (this.latestUpdatePending) { + this.latestDataUpdate(); + } + if (this.resizePending) { + this.resize(); + } + if (mapLoaded) { + mapLoaded(this.map); + } + } }); - this.ctx.defaultSubscription.subscribeAllForPaginatedData(this.pageLink, null); } map: LeafletMap; @@ -233,27 +255,27 @@ export class MapWidgetController implements MapWidgetInterface { } } - initSettings(settings: UnitedMapSettings, isEditMap?: boolean): WidgetUnitedMapSettings { + async initSettings(settings: UnitedMapSettings, isEditMap?: boolean): Promise { const functionParams = ['data', 'dsData', 'dsIndex']; this.provider = settings.provider || this.mapProvider; const parsedOptions: Partial = { provider: this.provider, - parsedLabelFunction: parseFunction(settings.labelFunction, functionParams), - parsedTooltipFunction: parseFunction(settings.tooltipFunction, functionParams), - parsedColorFunction: parseFunction(settings.colorFunction, functionParams), - parsedColorPointFunction: parseFunction(settings.colorPointFunction, functionParams), - parsedStrokeOpacityFunction: parseFunction(settings.strokeOpacityFunction, functionParams), - parsedStrokeWeightFunction: parseFunction(settings.strokeWeightFunction, functionParams), - parsedPolygonLabelFunction: parseFunction(settings.polygonLabelFunction, functionParams), - parsedPolygonColorFunction: parseFunction(settings.polygonColorFunction, functionParams), - parsedPolygonStrokeColorFunction: parseFunction(settings.polygonStrokeColorFunction, functionParams), - parsedPolygonTooltipFunction: parseFunction(settings.polygonTooltipFunction, functionParams), - parsedCircleLabelFunction: parseFunction(settings.circleLabelFunction, functionParams), - parsedCircleStrokeColorFunction: parseFunction(settings.circleStrokeColorFunction, functionParams), - parsedCircleFillColorFunction: parseFunction(settings.circleFillColorFunction, functionParams), - parsedCircleTooltipFunction: parseFunction(settings.circleTooltipFunction, functionParams), - parsedMarkerImageFunction: parseFunction(settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex']), - parsedClusterMarkerFunction: parseFunction(settings.clusterMarkerFunction, ['data', 'childCount']), + parsedLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.labelFunction, functionParams)), + parsedTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.tooltipFunction, functionParams)), + parsedColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.colorFunction, functionParams)), + parsedColorPointFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.colorPointFunction, functionParams)), + parsedStrokeOpacityFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.strokeOpacityFunction, functionParams)), + parsedStrokeWeightFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.strokeWeightFunction, functionParams)), + parsedPolygonLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonLabelFunction, functionParams)), + parsedPolygonColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonColorFunction, functionParams)), + parsedPolygonStrokeColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonStrokeColorFunction, functionParams)), + parsedPolygonTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.polygonTooltipFunction, functionParams)), + parsedCircleLabelFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleLabelFunction, functionParams)), + parsedCircleStrokeColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleStrokeColorFunction, functionParams)), + parsedCircleFillColorFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleFillColorFunction, functionParams)), + parsedCircleTooltipFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.circleTooltipFunction, functionParams)), + parsedMarkerImageFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.markerImageFunction, ['data', 'images', 'dsData', 'dsIndex'])), + parsedClusterMarkerFunction: await firstValueFrom(parseTbFunction(this.ctx.http, settings.clusterMarkerFunction, ['data', 'childCount'])), // labelColor: this.ctx.widgetConfig.color, // polygonLabelColor: this.ctx.widgetConfig.color, polygonKeyName: (settings as any).polKeyName ? (settings as any).polKeyName : settings.polygonKeyName, @@ -277,20 +299,36 @@ export class MapWidgetController implements MapWidgetInterface { } update() { + if (this.map) { + this.updatePending = false; this.map.updateData(this.drawRoutes); this.map.setLoading(false); + } else { + this.updatePending = true; + } } latestDataUpdate() { - this.map.updateData(this.drawRoutes); + if (this.map) { + this.latestUpdatePending = false; + this.map.updateData(this.drawRoutes); + } else { + this.latestUpdatePending = true; + } } resize() { - this.map.onResize(); - this.map?.invalidateSize(); + if (this.map) { + this.resizePending = false; + this.map.onResize(); + this.map.invalidateSize(); + } else { + this.resizePending = true; + } } destroy() { + this.destroyed = true; if (this.map) { this.map.remove(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts index 125bf4b5c4..d47ed8f110 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/maps-utils.ts @@ -15,13 +15,11 @@ /// import L from 'leaflet'; -import { - GenericFunction, - ShowTooltipAction, WidgetToolipSettings -} from './map-models'; +import { GenericFunction, ShowTooltipAction, WidgetToolipSettings } from './map-models'; import { Datasource, FormattedData } from '@app/shared/models/widget.models'; -import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecute } from '@core/utils'; +import { fillDataPattern, isDefinedAndNotNull, isString, processDataPattern, safeExecuteTbFunction } from '@core/utils'; import { parseWithTranslation } from '@home/components/widget/lib/maps/common-maps-utils'; +import { CompiledTbFunction } from '@shared/models/js-function.models'; export function createTooltip(target: L.Layer, settings: Partial, @@ -90,7 +88,7 @@ export function isJSON(data: string): boolean { export interface LabelSettings { showLabel: boolean; useLabelFunction: boolean; - parsedLabelFunction: GenericFunction; + parsedLabelFunction: CompiledTbFunction; label: string; } @@ -98,7 +96,7 @@ export function entitiesParseName(entities: FormattedData[], labelSettings: Labe const div = document.createElement('div'); for (const entity of entities) { if (labelSettings?.showLabel) { - const pattern = labelSettings.useLabelFunction ? safeExecute(labelSettings.parsedLabelFunction, + const pattern = labelSettings.useLabelFunction ? safeExecuteTbFunction(labelSettings.parsedLabelFunction, [entity, entities, entity.dsIndex]) : labelSettings.label; const markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); const replaceInfoLabelMarker = processDataPattern(pattern, entity); 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 e41928d9e9..d62b2b9217 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 @@ -19,7 +19,13 @@ import { MarkerIconInfo, MarkerIconReadyFunction, MarkerImageInfo, WidgetMarkers import { bindPopupActions, createTooltip } from './maps-utils'; import { loadImageWithAspect, parseWithTranslation } from './common-maps-utils'; import tinycolor from 'tinycolor2'; -import { fillDataPattern, isDefined, isDefinedAndNotNull, processDataPattern, safeExecute } from '@core/utils'; +import { + fillDataPattern, + isDefined, + isDefinedAndNotNull, + processDataPattern, + safeExecuteTbFunction +} from '@core/utils'; import LeafletMap from './leaflet-map'; import { FormattedData } from '@shared/models/widget.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; @@ -101,7 +107,7 @@ export class Marker { updateMarkerTooltip(data: FormattedData) { if (!this.map.markerTooltipText || this.settings.useTooltipFunction) { const pattern = this.settings.useTooltipFunction ? - safeExecute(this.settings.parsedTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; + safeExecuteTbFunction(this.settings.parsedTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.replaceInfoTooltipMarker = processDataPattern(this.map.markerTooltipText, data); } @@ -123,7 +129,7 @@ export class Marker { if (settings.showLabel) { if (!this.map.markerLabelText || settings.useLabelFunction) { const pattern = settings.useLabelFunction ? - safeExecute(settings.parsedLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; + safeExecuteTbFunction(settings.parsedLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.replaceInfoLabelMarker = processDataPattern(this.map.markerLabelText, this.data); } @@ -165,11 +171,11 @@ export class Marker { return; } const currentImage: MarkerImageInfo = this.settings.useMarkerImageFunction ? - safeExecute(this.settings.parsedMarkerImageFunction, + safeExecuteTbFunction(this.settings.parsedMarkerImageFunction, [this.data, this.settings.markerImages, this.dataSources, this.data.dsIndex]) : this.settings.currentImage; let currentColor = this.settings.tinyColor; if (this.settings.useColorFunction) { - const functionColor = safeExecute(this.settings.parsedColorFunction, + const functionColor = safeExecuteTbFunction(this.settings.parsedColorFunction, [this.data, this.dataSources, this.data.dsIndex]); if (isDefinedAndNotNull(functionColor)) { currentColor = tinycolor(functionColor); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts index aef95731e5..cb9ba7c35f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/polygon.ts @@ -16,13 +16,10 @@ import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet'; import { createTooltip, isCutPolygon } from './maps-utils'; -import { - functionValueCalculator, - parseWithTranslation -} from './common-maps-utils'; +import { functionValueCalculator, parseWithTranslation } from './common-maps-utils'; import { WidgetPolygonSettings } from './map-models'; import { FormattedData } from '@shared/models/widget.models'; -import { fillDataPattern, processDataPattern, safeExecute } from '@core/utils'; +import { fillDataPattern, processDataPattern, safeExecuteTbFunction } from '@core/utils'; import LeafletMap from '@home/components/widget/lib/maps/leaflet-map'; export class Polygon { @@ -92,7 +89,7 @@ export class Polygon { updateTooltip(data: FormattedData) { const pattern = this.settings.usePolygonTooltipFunction ? - safeExecute(this.settings.parsedPolygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : + safeExecuteTbFunction(this.settings.parsedPolygonTooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.polygonTooltipPattern; this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); } @@ -102,7 +99,7 @@ export class Polygon { if (settings.showPolygonLabel) { if (!this.map.polygonLabelText || settings.usePolygonLabelFunction) { const pattern = settings.usePolygonLabelFunction ? - safeExecute(settings.parsedPolygonLabelFunction, + safeExecuteTbFunction(settings.parsedPolygonLabelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.polygonLabel; this.map.polygonLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); this.map.replaceInfoLabelPolygon = processDataPattern(this.map.polygonLabelText, this.data); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index 8cf1822ad5..0abcc9f7c8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -23,16 +23,17 @@ import { PosFunction, WidgetUnitedMapSettings } from '../map-models'; -import { Observable, of, ReplaySubject, switchMap } from 'rxjs'; +import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction } from '@core/utils'; +import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; +import { CompiledTbFunction } from '@shared/models/js-function.models'; const maxZoom = 4; // ? @@ -43,13 +44,20 @@ export class ImageMap extends LeafletMap { width = 0; height = 0; imageUrl: string; - posFunction: PosFunction; + posFunction: CompiledTbFunction; constructor(ctx: WidgetContext, $container: HTMLElement, options: WidgetUnitedMapSettings) { super(ctx, $container, options); - this.posFunction = parseFunction(options.posFunction, - ['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']) as PosFunction; - this.mapImage(options).subscribe((mapImage) => { + + const initData = { + posFunction: parseTbFunction(this.ctx.http, options.posFunction, + ['origXPos', 'origYPos', 'data', 'dsData', 'dsIndex', 'aspect']), + mapImage: this.mapImage(options) + }; + + forkJoin(initData).subscribe(inited => { + this.posFunction = inited.posFunction; + const mapImage = inited.mapImage; this.imageUrl = mapImage.imageUrl; this.aspect = mapImage.aspect; if (mapImage.update) { @@ -272,7 +280,7 @@ export class ImageMap extends LeafletMap { convertPosition(data: FormattedData, dsData: FormattedData[]): L.LatLng { const position = this.extractPosition(data); if (position) { - const converted = this.posFunction(position.x, position.y, data, dsData, data.dsIndex, this.aspect) || {x: 0, y: 0}; + const converted = this.posFunction.execute(position.x, position.y, data, dsData, data.dsIndex, this.aspect) || {x: 0, y: 0}; return this.positionToLatLng(converted); } else { return null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html index d719c84e9c..57ce448999 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/circle-settings.component.html @@ -62,6 +62,7 @@ { - this.historicalData = formattedDataArrayFromDatasourceData(this.ctx.data).map( - item => this.clearIncorrectFirsLastDatapoint(item)).filter(arr => arr.length); - this.interpolatedTimeData.length = 0; - this.formattedInterpolatedTimeData.length = 0; - const prevMinTime = this.minTime; - const prevMaxTime = this.maxTime; - this.calculateIntervals(); - const currentTime = this.calculateCurrentTime(prevMinTime, prevMaxTime); - if (currentTime !== this.currentTime) { - this.timeUpdated(currentTime); - } - this.mapWidget.map.map?.invalidateSize(); - this.mapWidget.map.setLoading(false); - this.cd.detectChanges(); + this.update(); }; subscription.callbacks.onLatestDataUpdated = () => { - this.formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData); - this.updateCurrentData(); + this.latestDataUpdate(); }; + from(this.initializeFunctions()).subscribe(() => { + this.initialized = true; + if (this.updatePending) { + this.updateCurrentData(); + } + }); + } ngAfterViewInit() { import('@home/components/widget/lib/maps/map-widget2').then( (mod) => { - this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement); + this.mapWidget = new mod.MapWidgetController(MapProviders.openstreet, false, this.ctx, this.mapContainer.nativeElement, false, + () => { + if (this.mapWidgetUpdatePending) { + this.updateMapWidget(); + } + } + ); this.mapResize$ = new ResizeObserver(() => { this.mapWidget.resize(); }); @@ -177,22 +177,65 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy this.updateCurrentData(); } - private updateCurrentData() { - let currentPosition = this.formattedCurrentPosition; - if (this.formattedLatestData.length) { - currentPosition = mergeFormattedData(this.formattedCurrentPosition, this.formattedLatestData); + private async initializeFunctions(): Promise { + this.settings.parsedPointAsAnchorFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex'])); + this.settings.parsedTooltipFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.tooltipFunction, ['data', 'dsData', 'dsIndex'])); + this.settings.parsedLabelFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.labelFunction, ['data', 'dsData', 'dsIndex'])); + this.settings.parsedColorPointFunction = await firstValueFrom(parseTbFunction(this.ctx.http, this.settings.colorPointFunction, ['data', 'dsData', 'dsIndex'])); + } + + private update() { + this.historicalData = formattedDataArrayFromDatasourceData(this.ctx.data).map( + item => this.clearIncorrectFirsLastDatapoint(item)).filter(arr => arr.length); + this.interpolatedTimeData.length = 0; + this.formattedInterpolatedTimeData.length = 0; + const prevMinTime = this.minTime; + const prevMaxTime = this.maxTime; + this.calculateIntervals(); + const currentTime = this.calculateCurrentTime(prevMinTime, prevMaxTime); + if (currentTime !== this.currentTime) { + this.timeUpdated(currentTime); } - this.calcLabel(currentPosition); - this.calcMainTooltip(currentPosition); - if (this.mapWidget && this.mapWidget.map && this.mapWidget.map.map) { - this.mapWidget.map.updateFromData(true, currentPosition, this.formattedInterpolatedTimeData, (trip) => { - this.activeTrip = trip; - this.timeUpdated(this.currentTime); - this.cd.markForCheck(); - }); - if (this.settings.showPoints) { - this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip); + this.updateMapWidget(); + } + + private latestDataUpdate() { + this.formattedLatestData = formattedDataFormDatasourceData(this.ctx.latestData); + this.updateCurrentData(); + } + + private updateMapWidget() { + if (this.mapWidget?.map) { + this.mapWidgetUpdatePending = false; + this.mapWidget.map.map?.invalidateSize(); + this.mapWidget.map.setLoading(false); + this.cd.detectChanges(); + } else { + this.mapWidgetUpdatePending = true; + } + } + + private updateCurrentData() { + if (this.initialized) { + this.updatePending = false; + let currentPosition = this.formattedCurrentPosition; + if (this.formattedLatestData.length) { + currentPosition = mergeFormattedData(this.formattedCurrentPosition, this.formattedLatestData); } + this.calcLabel(currentPosition); + this.calcMainTooltip(currentPosition); + if (this.mapWidget?.map?.map) { + this.mapWidget.map.updateFromData(true, currentPosition, this.formattedInterpolatedTimeData, (trip) => { + this.activeTrip = trip; + this.timeUpdated(this.currentTime); + this.cd.markForCheck(); + }); + if (this.settings.showPoints) { + this.mapWidget.map.updatePoints(this.formattedInterpolatedTimeData, this.calcTooltip); + } + } + } else { + this.updatePending = true; } } @@ -235,7 +278,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy if (this.useAnchors) { const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]); this.anchors = anchorDate - .filter((data: [string, FormattedData], tsIndex) => safeExecute(this.settings.parsedPointAsAnchorFunction, [data[1], + .filter((data: [string, FormattedData], tsIndex) => safeExecuteTbFunction(this.settings.parsedPointAsAnchorFunction, [data[1], this.formattedInterpolatedTimeData.map(ds => ds[tsIndex]), data[1].dsIndex])) .map(data => parseInt(data[0], 10)); } @@ -244,7 +287,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy calcTooltip = (point: FormattedData, points: FormattedData[]): string => { const data = point ? point : this.activeTrip; const tooltipPattern: string = this.settings.useTooltipFunction ? - safeExecute(this.settings.parsedTooltipFunction, + safeExecuteTbFunction(this.settings.parsedTooltipFunction, [data, points, point.dsIndex]) : this.settings.tooltipPattern; return parseWithTranslation.parseTemplate(tooltipPattern, data, true); } @@ -261,7 +304,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy if (this.activeTrip) { const data = points[this.activeTrip.dsIndex]; const labelText: string = this.settings.useLabelFunction ? - safeExecute(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label; + safeExecuteTbFunction(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label; this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true)); } } From f50789ebf3c2be362e788071f384302d2a1a84a4 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 3 Dec 2024 17:17:49 +0200 Subject: [PATCH 015/103] Minor refactoring for ResourceService --- .../install/update/ResourcesUpdater.java | 4 +- .../resource/DefaultTbResourceService.java | 15 +++-- .../server/dao/resource/ResourceService.java | 8 +-- .../dao/dashboard/DashboardServiceImpl.java | 11 ++-- .../dao/resource/BaseResourceService.java | 63 ++++++++++--------- .../dao/widget/WidgetTypeServiceImpl.java | 9 +-- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java index 00797da449..da2b7276dd 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java @@ -101,7 +101,7 @@ public class ResourcesUpdater { for (DashboardId dashboardId : dashboards) { executor.submit(() -> { Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId); - boolean updated = resourceService.updateResourcesUsage(dashboard); // will convert resources ids to new structure + boolean updated = resourceService.updateResourcesUsage(dashboard.getTenantId(), dashboard); // will convert resources ids to new structure if (updated) { dashboardService.saveDashboard(dashboard); updatedCount.incrementAndGet(); @@ -130,7 +130,7 @@ public class ResourcesUpdater { for (WidgetTypeId widgetTypeId : widgets) { executor.submit(() -> { WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId); - boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails); + boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails); if (updated) { widgetTypeService.saveWidgetType(widgetTypeDetails); updatedCount.incrementAndGet(); diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 90ad3e38dd..e3a79a8ba5 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -46,7 +46,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -132,12 +132,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List exportResources(Dashboard dashboard, SecurityUser user) throws ThingsboardException { - return exportResources(dashboard, imageService::getUsedImages, resourceService::getUsedResources, user); + return exportResources(() -> imageService.getUsedImages(dashboard), () -> resourceService.getUsedResources(user.getTenantId(), dashboard), user); } @Override public List exportResources(WidgetTypeDetails widgetTypeDetails, SecurityUser user) throws ThingsboardException { - return exportResources(widgetTypeDetails, imageService::getUsedImages, resourceService::getUsedResources, user); + return exportResources(() -> imageService.getUsedImages(widgetTypeDetails), () -> resourceService.getUsedResources(user.getTenantId(), widgetTypeDetails), user); } @Override @@ -153,13 +153,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements } } - private List exportResources(T entity, - Function> imagesProcessor, - Function> resourcesProcessor, + private List exportResources(Supplier> imagesProcessor, + Supplier> resourcesProcessor, SecurityUser user) throws ThingsboardException { List resources = new ArrayList<>(); - resources.addAll(imagesProcessor.apply(entity)); - resources.addAll(resourcesProcessor.apply(entity)); + resources.addAll(imagesProcessor.get()); + resources.addAll(resourcesProcessor.get()); for (TbResourceInfo resourceInfo : resources) { accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, resourceInfo.getId(), resourceInfo); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 0e2acb2e4e..f7f4b1ce4b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -80,13 +80,13 @@ public interface ResourceService extends EntityDaoService { TbResourceInfo findSystemOrTenantResourceByEtag(TenantId tenantId, ResourceType resourceType, String etag); - boolean updateResourcesUsage(Dashboard dashboard); + boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard); - boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails); + boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails); - Collection getUsedResources(Dashboard dashboard); + Collection getUsedResources(TenantId tenantId, Dashboard dashboard); - Collection getUsedResources(WidgetTypeDetails widgetTypeDetails); + Collection getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails); TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, byte[] data); diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index b547de1eca..2e9e37577c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -162,18 +162,19 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); } try { + TenantId tenantId = dashboard.getTenantId(); if (CollectionUtils.isNotEmpty(dashboard.getResources())) { - resourceService.importResources(dashboard.getTenantId(), dashboard.getResources()); + resourceService.importResources(tenantId, dashboard.getResources()); } imageService.updateImagesUsage(dashboard); - resourceService.updateResourcesUsage(dashboard); + resourceService.updateResourcesUsage(tenantId, dashboard); - var saved = dashboardDao.save(dashboard.getTenantId(), dashboard); + var saved = dashboardDao.save(tenantId, dashboard); publishEvictEvent(new DashboardTitleEvictEvent(saved.getId())); - eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId()) + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) .entityId(saved.getId()).created(dashboard.getId() == null).build()); if (dashboard.getId() == null) { - countService.publishCountEntityEvictEvent(saved.getTenantId(), EntityType.DASHBOARD); + countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD); } return saved; } catch (Exception e) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 7101922f6b..361d2b3894 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -402,13 +402,13 @@ public class BaseResourceService extends AbstractCachedEntityService links = getResourcesLinks(dashboard.getResources()); - return updateResourcesUsage(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); + return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @Override - public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) { + public boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { Map links = getResourcesLinks(widgetTypeDetails.getResources()); List jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); @@ -422,7 +422,7 @@ public class BaseResourceService extends AbstractCachedEntityService getUsedResources(Dashboard dashboard) { - return getUsedResources(dashboard.getTenantId(), List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); + public Collection getUsedResources(TenantId tenantId, Dashboard dashboard) { + return getUsedResources(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING)).values(); } @Override - public Collection getUsedResources(WidgetTypeDetails widgetTypeDetails) { + public Collection getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { List jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); @@ -492,7 +492,7 @@ public class BaseResourceService extends AbstractCachedEntityService getUsedResources(TenantId tenantId, List jsonNodes, List> mappings) { @@ -543,30 +543,33 @@ public class BaseResourceService extends AbstractCachedEntityService { - String value = null; - if (urlNode.isTextual()) { // link is in the right place - value = urlNode.asText(); - } else { - JsonNode id = urlNode.get("id"); // old structure is used - if (id != null && id.isTextual()) { - value = id.asText(); + if (i <= mappings.size() - 1) { + JacksonUtil.replaceByMapping(jsonNode, mappings.get(i), Collections.emptyMap(), (name, urlNode) -> { + String value = null; + if (urlNode.isTextual()) { // link is in the right place + value = urlNode.asText(); + } else { + JsonNode id = urlNode.get("id"); // old structure is used + if (id != null && id.isTextual()) { + value = id.asText(); + } } - } - if (StringUtils.isNotBlank(value)) { - value = processor.apply(value); - } else { - value = ""; - } + if (StringUtils.isNotBlank(value)) { + value = processor.apply(value); + } else { + value = ""; + } + + JsonNode newValue = new TextNode(value); + if (!newValue.toString().equals(urlNode.toString())) { + updated.set(true); + log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); + } + return newValue; + }); + } - JsonNode newValue = new TextNode(value); - if (!newValue.toString().equals(urlNode.toString())) { - updated.set(true); - log.trace("Replaced by mapping '{}' ({}) with '{}'", value, name, newValue); - } - return newValue; - }); // processing all JacksonUtil.replaceAll(jsonNode, "", (name, value) -> { @@ -597,7 +600,7 @@ public class BaseResourceService extends AbstractCachedEntityService Date: Tue, 3 Dec 2024 17:26:53 +0100 Subject: [PATCH 016/103] updated bouncycastle version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c03465ba07..8dc3e1c40a 100755 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 2.2.21 0.8 1.19.0 - 1.78 + 1.78.1 2.0.1 42.7.3 org/thingsboard/server/gen/**/*, From 290e2faf6a79c3505c92b873d7288dacbb9c5ffe Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 3 Dec 2024 19:24:56 +0200 Subject: [PATCH 017/103] UI: Add JS modules support to get/set value converters of behavior. --- .../widget/lib/action/action-widget.models.ts | 118 +++++++++++------- ...value-action-settings-panel.component.html | 1 + ...value-action-settings-panel.component.html | 1 + .../scada-symbol-behavior-row.component.ts | 3 +- .../models/action-widget-settings.models.ts | 5 +- 5 files changed, 82 insertions(+), 46 deletions(-) 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 38d704d51b..b4a46d9592 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 @@ -22,7 +22,17 @@ import { telemetryTypeTranslationsShort } from '@shared/models/telemetry/telemetry.models'; import { WidgetContext } from '@home/models/widget-component.models'; -import { BehaviorSubject, forkJoin, Observable, Observer, of, Subscription, throwError } from 'rxjs'; +import { + BehaviorSubject, + forkJoin, + Observable, + Observer, + of, + ReplaySubject, + Subscription, + switchMap, + throwError +} from 'rxjs'; import { catchError, delay, map, share, take } from 'rxjs/operators'; import { AfterViewInit, ChangeDetectorRef, Directive, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { @@ -45,6 +55,8 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m import { EntityId } from '@shared/models/id/entity-id'; import { isDefinedAndNotNull } from '@core/utils'; import { parseError } from '@shared/models/error.models'; +import { CompiledTbFunction, compileTbFunction } from '@shared/models/js-function.models'; +import { HttpClient } from '@angular/common/http'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix @@ -163,46 +175,57 @@ type DataToValueFunction = (data: any) => V; export class DataToValueConverter { - private readonly dataToValueFunction: DataToValueFunction; + private readonly dataToValueFunction$: Observable>>; private readonly compareToValue: any; - constructor(private settings: DataToValueSettings, + constructor(private http: HttpClient, + 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; - } + this.dataToValueFunction$ = compileTbFunction(this.http, settings.dataToValueFunction, 'data').pipe( + catchError(() => { + return of(new CompiledTbFunction((data: any) => data, [])); + }), + share({ + connector: () => new ReplaySubject(1), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) + ); break; case DataToValueType.NONE: break; } } - dataToValue(data: any): V { - let result: V; + dataToValue(data: any): Observable { + let result: Observable; switch (this.settings.type) { case DataToValueType.FUNCTION: - result = data; - try { - let input = data; - if (!!data) { - try { - input = JSON.parse(data); - } catch (_e) {} - } - result = this.dataToValueFunction(input); - } catch (_e) {} + result = this.dataToValueFunction$.pipe( + map((dataToValueFunction) => { + let input = data; + if (!!data) { + try { + input = JSON.parse(data); + } catch (_e) {} + } + return dataToValueFunction.execute(input); + }), + catchError(() => of(data)) + ); break; case DataToValueType.NONE: - result = data; + result = of(data); break; } if (this.valueType === ValueType.BOOLEAN) { - result = (result === this.compareToValue) as any; + result = result.pipe( + map(val => (val === this.compareToValue) as V) + ); } return result; } @@ -260,17 +283,17 @@ export abstract class ValueGetter extends ValueAction { protected simulated: boolean) { super(ctx, settings); if (this.settings.action !== GetValueAction.DO_NOTHING && this.settings.action !== GetValueAction.GET_ALARM_STATUS) { - this.dataConverter = new DataToValueConverter(settings.dataToValue, valueType); + this.dataConverter = new DataToValueConverter(ctx.http, settings.dataToValue, valueType); } } getValue(): Observable { const valueObservable: Observable = this.doGetValue().pipe( - map((data) => { + switchMap((data) => { if (this.dataConverter) { return this.dataConverter.dataToValue(data); } else { - return data; + return of(data); } }), catchError(err => { @@ -308,9 +331,10 @@ type ValueToDataFunction = (value: V) => any; export class ValueToDataConverter { private readonly constantValue: any; - private readonly valueToDataFunction: ValueToDataFunction; + private readonly valueToDataFunction$: Observable>>; - constructor(protected settings: ValueToDataSettings) { + constructor(private http: HttpClient, + private settings: ValueToDataSettings) { switch (settings.type) { case ValueToDataType.VALUE: break; @@ -318,31 +342,38 @@ export class ValueToDataConverter { 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; - } + this.valueToDataFunction$ = compileTbFunction(this.http, settings.valueToDataFunction, 'value').pipe( + catchError(() => { + return of(new CompiledTbFunction((value: any) => value, [])); + }), + share({ + connector: () => new ReplaySubject(1), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) + ); break; case ValueToDataType.NONE: break; } } - valueToData(value: V): any { + valueToData(value: V): Observable { switch (this.settings.type) { case ValueToDataType.VALUE: - return value; + return of(value); case ValueToDataType.CONSTANT: - return this.constantValue; + return of(this.constantValue); case ValueToDataType.FUNCTION: - let result = value; - try { - result = this.valueToDataFunction(value); - } catch (e) {} - return result; + return this.valueToDataFunction$.pipe( + map((valueToDataFunction) => { + return valueToDataFunction.execute(value); + }), + catchError(() => of(value)) + ); case ValueToDataType.NONE: - return null; + return of(null); } } } @@ -368,14 +399,15 @@ export abstract class ValueSetter extends ValueAction { protected settings: SetValueSettings, protected simulated: boolean) { super(ctx, settings); - this.valueToDataConverter = new ValueToDataConverter(settings.valueToData); + this.valueToDataConverter = new ValueToDataConverter(ctx.http, settings.valueToData); } setValue(value: V): Observable { if (this.simulated) { return of(null).pipe(delay(500)); } else { - return this.doSetValue(this.valueToDataConverter.valueToData(value)).pipe( + return this.valueToDataConverter.valueToData(value).pipe( + switchMap(data => this.doSetValue(data)), catchError(err => { throw this.handleError(err); }) 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 87a81db0eb..cb6cd5cbe0 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 @@ -138,6 +138,7 @@
{ if (!behavior.id || !behavior.name || !behavior.type) { @@ -77,7 +78,7 @@ export const behaviorValid = (behavior: ScadaSymbolBehavior): boolean => { return false; } if (behavior.defaultSetValueSettings.valueToData?.type === ValueToDataType.FUNCTION - && isUndefinedOrNull(behavior.defaultSetValueSettings.valueToData?.valueToDataFunction)) { + && !isNotEmptyTbFunction(behavior.defaultSetValueSettings.valueToData?.valueToDataFunction)) { return false; } break; 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 fc03ef2eb9..b1c47a1fd3 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 @@ -17,6 +17,7 @@ import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { widgetType } from '@shared/models/widget.models'; import { AlarmSeverity } from '@shared/models/alarm.models'; +import { TbFunction } from '@shared/models/js-function.models'; export enum GetValueAction { DO_NOTHING = 'DO_NOTHING', @@ -79,7 +80,7 @@ export enum DataToValueType { export interface DataToValueSettings { type: DataToValueType; - dataToValueFunction: string; + dataToValueFunction: TbFunction; compareToValue?: any; } @@ -131,7 +132,7 @@ export enum ValueToDataType { export interface ValueToDataSettings { type: ValueToDataType; constantValue: any; - valueToDataFunction: string; + valueToDataFunction: TbFunction; } export interface SetValueSettings extends ValueActionSettings { From a0d2ece2bb873158f2051f0a65e5bad8faa9d4ee Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Wed, 4 Dec 2024 10:08:41 +0200 Subject: [PATCH 018/103] UI: Fixed disable event on edit mode --- .../widget/lib/button/two-segment-button-widget.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html index 10cf016e11..1aac07bd4a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/button/two-segment-button-widget.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
From 18eab054a437462bc55d51daca2a679c0e934048 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 4 Dec 2024 11:35:35 +0200 Subject: [PATCH 019/103] tbel: ver 1.2.5 add bitwise operation for boolean to MathProcessor --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a1d5f26ecf..05f16ca5b1 100755 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 3.9.2 3.25.3 1.63.0 - 1.2.4 + 1.2.5 1.18.32 1.2.5 1.2.5 From fde6756bcad4dd0a302491b2ca3a05a926eca2cb Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 4 Dec 2024 13:13:45 +0200 Subject: [PATCH 020/103] UI: Added to JavaScript library details audit logs and version control --- .../vc/entity-version-create.component.html | 4 +-- .../vc/entity-version-create.component.ts | 9 +++-- .../modules/home/pages/admin/admin.module.ts | 2 ++ .../js-library-table-config.resolver.ts | 2 ++ .../resource/resource-tabs.component.html | 27 ++++++++++++++ .../admin/resource/resource-tabs.component.ts | 36 +++++++++++++++++++ ui-ngx/src/app/shared/models/vc.models.ts | 12 +++---- 7 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.ts diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html index 67ef5908e5..ab127e45cd 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html @@ -41,10 +41,10 @@ {{ 'version-control.export-credentials' | translate }} - + {{ 'version-control.export-attributes' | translate }} - + {{ 'version-control.export-relations' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts index 26d45de2c0..7d3e89eda8 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts @@ -18,6 +18,7 @@ import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular import { PageComponent } from '@shared/components/page.component'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { + entityTypesWithoutRelatedData, SingleEntityVersionCreateRequest, VersionCreateRequestType, VersionCreationResult @@ -62,6 +63,8 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni entityTypes = EntityType; + entityTypesWithoutRelatedData = entityTypesWithoutRelatedData; + resultMessage: string; versionCreateResult$: Observable; @@ -108,8 +111,10 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni branch: this.createVersionFormGroup.get('branch').value, versionName: this.createVersionFormGroup.get('versionName').value, config: { - saveRelations: this.createVersionFormGroup.get('saveRelations').value, - saveAttributes: this.createVersionFormGroup.get('saveAttributes').value, + saveRelations: !entityTypesWithoutRelatedData.has(this.entityId.entityType) + ? this.createVersionFormGroup.get('saveRelations').value : false, + saveAttributes: !entityTypesWithoutRelatedData.has(this.entityId.entityType) + ? this.createVersionFormGroup.get('saveAttributes').value : false, saveCredentials: this.entityId.entityType === EntityType.DEVICE ? this.createVersionFormGroup.get('saveCredentials').value : false }, type: VersionCreateRequestType.SINGLE_ENTITY diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts index 704f909809..784aade083 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts @@ -27,6 +27,7 @@ import { SmsProviderComponent } from '@home/pages/admin/sms-provider.component'; import { SendTestSmsDialogComponent } from '@home/pages/admin/send-test-sms-dialog.component'; import { HomeSettingsComponent } from '@home/pages/admin/home-settings.component'; import { ResourcesLibraryComponent } from '@home/pages/admin/resource/resources-library.component'; +import { ResourceTabsComponent } from '@home/pages/admin/resource/resource-tabs.component'; import { ResourcesTableHeaderComponent } from '@home/pages/admin/resource/resources-table-header.component'; import { QueueComponent } from '@home/pages/admin/queue/queue.component'; import { RepositoryAdminSettingsComponent } from '@home/pages/admin/repository-admin-settings.component'; @@ -47,6 +48,7 @@ import { NgxFlowModule } from '@flowjs/ngx-flow'; SecuritySettingsComponent, HomeSettingsComponent, ResourcesLibraryComponent, + ResourceTabsComponent, ResourcesTableHeaderComponent, JsResourceComponent, JsLibraryTableHeaderComponent, diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/js-library-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/js-library-table-config.resolver.ts index b0fadd53d9..862adf48d5 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/js-library-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/js-library-table-config.resolver.ts @@ -43,6 +43,7 @@ import { EntityAction } from '@home/models/entity/entity-component.models'; import { JsLibraryTableHeaderComponent } from '@home/pages/admin/resource/js-library-table-header.component'; import { JsResourceComponent } from '@home/pages/admin/resource/js-resource.component'; import { switchMap } from 'rxjs/operators'; +import { ResourceTabsComponent } from '@home/pages/admin/resource/resource-tabs.component'; @Injectable() export class JsLibraryTableConfigResolver { @@ -57,6 +58,7 @@ export class JsLibraryTableConfigResolver { this.config.entityType = EntityType.TB_RESOURCE; this.config.entityComponent = JsResourceComponent; + this.config.entityTabsComponent = ResourceTabsComponent; this.config.entityTranslations = { details: 'javascript.javascript-resource-details', add: 'javascript.add', diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.html new file mode 100644 index 0000000000..fb608683d8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.html @@ -0,0 +1,27 @@ + + + + + + + diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.ts new file mode 100644 index 0000000000..2cd2f43d62 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resource-tabs.component.ts @@ -0,0 +1,36 @@ +/// +/// 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 { EntityTabsComponent } from '@home/components/entity/entity-tabs.component'; +import { Resource } from '@shared/models/resource.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; + +@Component({ + selector: 'tb-resource-tabs', + templateUrl: './resource-tabs.component.html', + styleUrls: [] +}) +export class ResourceTabsComponent extends EntityTabsComponent { + + readonly NULL_UUID = NULL_UUID; + + constructor(protected store: Store) { + super(store); + } +} diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index ba54a297a7..15d41656df 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -15,7 +15,7 @@ /// import { EntityId } from '@shared/models/id/entity-id'; -import { EntityType } from '@shared/models/entity-type.models'; +import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { ExportableEntity } from '@shared/models/base-data'; import { EntityRelation } from '@shared/models/relation.models'; import { Device, DeviceCredentials } from '@shared/models/device.models'; @@ -38,7 +38,7 @@ export const exportableEntityTypes: Array = [ EntityType.NOTIFICATION_RULE ]; -export const entityTypesWithoutRelatedData: Set = new Set([ +export const entityTypesWithoutRelatedData: Set = new Set([ EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE, @@ -104,8 +104,8 @@ export function createDefaultEntityTypesVersionCreate(): {[entityType: string]: for (const entityType of exportableEntityTypes) { res[entityType] = { syncStrategy: null, - saveAttributes: true, - saveRelations: true, + saveAttributes: !entityTypesWithoutRelatedData.has(entityType), + saveRelations: !entityTypesWithoutRelatedData.has(entityType), saveCredentials: true, allEntities: true, entityIds: [] @@ -151,8 +151,8 @@ export function createDefaultEntityTypesVersionLoad(): {[entityType: string]: En const res: {[entityType: string]: EntityTypeVersionLoadConfig} = {}; for (const entityType of exportableEntityTypes) { res[entityType] = { - loadAttributes: true, - loadRelations: true, + loadAttributes: !entityTypesWithoutRelatedData.has(entityType), + loadRelations: !entityTypesWithoutRelatedData.has(entityType), loadCredentials: true, removeOtherEntities: false, findExistingEntityByName: true From 749df3f212e3da715a82feb81728a548c92dcd0e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 15:19:42 +0200 Subject: [PATCH 021/103] Add gzip compression for load dashboard and download svg images methods. --- .../server/controller/BaseController.java | 21 ++++++++++ .../controller/DashboardController.java | 36 +++++++++++------ .../server/controller/ImageController.java | 39 +++++++++++++------ 3 files changed, 74 insertions(+), 22 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 8470566af3..2d868a1135 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; import jakarta.mail.MessagingException; +import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.ConstraintViolation; import lombok.Getter; @@ -28,6 +29,7 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -182,7 +184,9 @@ import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; +import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -194,6 +198,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; import static org.thingsboard.server.common.data.StringUtils.isNotEmpty; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; @@ -1008,6 +1013,22 @@ public abstract class BaseController { } } + protected void compressResponseWithGzipIFAccepted(String acceptEncodingHeader, HttpServletResponse response, byte[] content) throws IOException { + if (StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) { + response.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip"); + response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())) { + gzipOutputStream.write(content); + gzipOutputStream.finish(); + } + } else { + try (ServletOutputStream outputStream = response.getOutputStream()) { + outputStream.write(content); + outputStream.flush(); + } + } + } + protected ResponseEntity response(HttpStatus status) { return ResponseEntity.status(status).build(); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index e3e793014d..e50484bed3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -22,8 +22,10 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; @@ -31,6 +33,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -67,6 +70,7 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION; @@ -149,17 +153,20 @@ public class DashboardController extends BaseController { ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @GetMapping(value = "/dashboard/{dashboardId}") - public Dashboard getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) + public void getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION) @PathVariable(DASHBOARD_ID) String strDashboardId, @Parameter(description = INCLUDE_RESOURCES_DESCRIPTION) - @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources) throws ThingsboardException { + @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, + HttpServletResponse response) throws Exception { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ); if (includeResources) { dashboard.setResources(tbResourceService.exportResources(dashboard, getCurrentUser())); } - return dashboard; + response.setContentType(APPLICATION_JSON_VALUE); + compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(dashboard)); } @ApiOperation(value = "Create Or Update Dashboard (saveDashboard)", @@ -171,11 +178,15 @@ public class DashboardController extends BaseController { TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @PostMapping(value = "/dashboard") - public Dashboard saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.") - @RequestBody Dashboard dashboard) throws Exception { + public void saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.") + @RequestBody Dashboard dashboard, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, + HttpServletResponse response) throws Exception { dashboard.setTenantId(getTenantId()); checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); - return tbDashboardService.save(dashboard, getCurrentUser()); + var savedDashboard = tbDashboardService.save(dashboard, getCurrentUser()); + response.setContentType(APPLICATION_JSON_VALUE); + compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(savedDashboard)); } @ApiOperation(value = "Delete the Dashboard (deleteDashboard)", @@ -408,12 +419,13 @@ public class DashboardController extends BaseController { "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) - @ResponseBody - public HomeDashboard getHomeDashboard() throws ThingsboardException { + @GetMapping(value = "/dashboard/home") + public void getHomeDashboard(@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader, + HttpServletResponse response) throws Exception { SecurityUser securityUser = getCurrentUser(); + response.setContentType(APPLICATION_JSON_VALUE); if (securityUser.isSystemAdmin()) { - return null; + return; } User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId()); JsonNode additionalInfo = user.getAdditionalInfo(); @@ -431,7 +443,9 @@ public class DashboardController extends BaseController { homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo); } } - return homeDashboard; + if (homeDashboard != null) { + compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(homeDashboard)); + } } @ApiOperation(value = "Get Home Dashboard Info (getHomeDashboardInfo)", diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index cd1116a420..5a7bd19d8e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -61,7 +61,9 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.io.ByteArrayOutputStream; import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPOutputStream; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; @@ -70,6 +72,7 @@ import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INC import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; +import static org.thingsboard.server.dao.util.ImageUtils.mediaTypeToFileExtension; @Slf4j @RestController @@ -179,15 +182,17 @@ public class ImageController extends BaseController { @PathVariable String type, @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true) @PathVariable String key, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { - return downloadIfChanged(type, key, etag, false); + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception { + return downloadIfChanged(type, key, etag, acceptEncodingHeader, false); } @GetMapping(value = "/api/images/public/{publicResourceKey}", produces = "image/*") public ResponseEntity downloadPublicImage(@PathVariable String publicResourceKey, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception { ImageCacheKey cacheKey = ImageCacheKey.forPublicImage(publicResourceKey); - return downloadIfChanged(cacheKey, etag, () -> imageService.getPublicImageInfoByKey(publicResourceKey)); + return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> imageService.getPublicImageInfoByKey(publicResourceKey)); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -213,8 +218,9 @@ public class ImageController extends BaseController { @PathVariable String type, @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true) @PathVariable String key, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { - return downloadIfChanged(type, key, etag, true); + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag, + @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception { + return downloadIfChanged(type, key, etag, acceptEncodingHeader, true); } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -268,12 +274,12 @@ public class ImageController extends BaseController { return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result); } - private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws Exception { + private ResponseEntity downloadIfChanged(String type, String key, String etag, String acceptEncodingHeader, boolean preview) throws Exception { ImageCacheKey cacheKey = ImageCacheKey.forImage(getTenantId(type), key, preview); - return downloadIfChanged(cacheKey, etag, () -> checkImageInfo(type, key, Operation.READ)); + return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> checkImageInfo(type, key, Operation.READ)); } - private ResponseEntity downloadIfChanged(ImageCacheKey cacheKey, String etag, ThrowingSupplier imageInfoSupplier) throws Exception { + private ResponseEntity downloadIfChanged(ImageCacheKey cacheKey, String etag, String acceptEncodingHeader, ThrowingSupplier imageInfoSupplier) throws Exception { if (StringUtils.isNotEmpty(etag)) { etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification if (etag.equals(tbImageService.getETag(cacheKey))) { @@ -294,7 +300,6 @@ public class ImageController extends BaseController { tbImageService.putETag(cacheKey, descriptor.getEtag()); var result = ResponseEntity.ok() .header("Content-Type", descriptor.getMediaType()) - .contentLength(data.length) .eTag(descriptor.getEtag()); if (!cacheKey.isPublic()) { result @@ -308,7 +313,19 @@ public class ImageController extends BaseController { } else { result.cacheControl(CacheControl.noCache()); } - return result.body(new ByteArrayResource(data)); + var responseData = data; + if (mediaTypeToFileExtension(descriptor.getMediaType()).equals("svg") && + StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) { + result.header(HttpHeaders.CONTENT_ENCODING, "gzip"); + var outputStream = new ByteArrayOutputStream(); + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) { + gzipOutputStream.write(data); + gzipOutputStream.finish(); + } + responseData = outputStream.toByteArray(); + } + result.contentLength(responseData.length); + return result.body(new ByteArrayResource(responseData)); } private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException { From cca4ac362950cb34a9360dc112076fe2ee3be0d0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:44:31 +0200 Subject: [PATCH 022/103] Version set to 3.9.0-RC --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 17b179c767..ef57037448 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index cc19cc949f..cbbce12336 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 1ecb95c6c5..a4f7dd4f7b 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index bd7dc214cf..4bbe65d99f 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 401d1e94fd..32da47ca08 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 62c1e420c2..7d48a63903 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index b206f0b173..e6eca1aa93 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 31d90de948..68e647642d 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index ac56095e7e..0614c94bc4 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index c905e0eee9..8bd1904d72 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 4409d6b97f..fded7298e4 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 7485294f6e..fab4ff3989 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 88e35e8429..1aad1dd39e 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 7309e6af51..38a14cd8f3 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 08ef60713e..4210f1f528 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 31fbc72dcc..231811144a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index d4528944e0..0bd4c0d6e3 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 054fc3cbbe..5a0dd9e4ff 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 54232ac104..ddb9d7a399 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index e672f7a5dd..45d228f225 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index c87fa8c2bf..6c4310151d 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 78665bfde2..a2b9dd7c84 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 7d221c48bf..8f01cab08b 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index ad3bbaba58..194724b70f 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index f37139b3c5..8e3fceff99 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 1dac54c142..c1f5a1f62b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 013a0b5c47..e19acb0fe6 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 4d34a13f38..5563fcc8d8 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index f747db1438..daea52bfa5 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 3a4834236c..20f8d90695 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index d77384cc74..057f37a575 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 11295dc4b9..3ca22bcf55 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 79a2a66385..3789fe14a1 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 9ec8589d9a..089581f1cb 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index d562f89cd5..4d9e17df5f 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 992187316a..94b86d035f 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 32aa6a4f23..2b637ddd19 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 34be0bd338..4f7e0ad030 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 95919ad175..94bc03f29d 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.9.0-SNAPSHOT + 3.9.0-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 8aeeed2b1e..f6dc43ea13 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 0932bf7c06..725c3e40ad 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 195ae123f4..c5a900e004 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 7de766b2da..d4cd55629d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard netty-mqtt - 3.9.0-SNAPSHOT + 3.9.0-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 8dc3e1c40a..094c48fd5d 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 069f7b84f0..f868acabf5 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index e6abae70ef..fbefcd1de9 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 57bc5771d2..3ee6496452 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 99853b0950..33bb15af00 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 2d196cec7d..dce8e1606d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 76655178ae..eef23eae9b 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 7e569ea1f1..d841c56da7 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index b5b7867146..cec30fcbc5 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 88009474aa..edf3727320 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 8e7f77be9b..d744f7eb2f 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 02fe36f657..dcd08c10af 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index f16e352d71..1384b1c3e6 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 3.9.0-RC thingsboard org.thingsboard From 8ac0468eddea77f0986cb74dff335964aa3af3a8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:51:03 +0200 Subject: [PATCH 023/103] Version set to 4.0.0-SNAPSHOT --- application/pom.xml | 2 +- application/src/main/resources/thingsboard.yml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/docker/install-tb.sh | 3 --- msa/tb/docker/upgrade-tb.sh | 13 ------------- msa/tb/pom.xml | 3 +-- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 62 files changed, 61 insertions(+), 78 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 17b179c767..112cc82ff7 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard application diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f642d7fea5..ef6d2204d8 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -206,7 +206,7 @@ ui: # Help parameters help: # Base URL for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.9}" + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.0}" # Database telemetry parameters database: diff --git a/common/actor/pom.xml b/common/actor/pom.xml index cc19cc949f..9b0053e0b6 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 1ecb95c6c5..fd9802a689 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index bd7dc214cf..d53ac4904d 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 401d1e94fd..30001c929d 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 62c1e420c2..0bd0f476ec 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index b206f0b173..f7c559c728 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 31d90de948..4f0fe5aac0 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index ac56095e7e..0e290d1e01 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index c905e0eee9..4c8f56cbce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 4409d6b97f..9a9b262d82 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 7485294f6e..6fad5efd5a 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 88e35e8429..8c4ea360cf 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 7309e6af51..2b780bf489 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 08ef60713e..ed93701513 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 31fbc72dcc..7cc67d5821 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index d4528944e0..86459e9821 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 054fc3cbbe..8ab2243ceb 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 54232ac104..a6e7209f21 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index e672f7a5dd..d074afc6b5 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index c87fa8c2bf..fdd3ad21d5 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 78665bfde2..5d4d37547f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 7d221c48bf..e2f03b060a 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index ad3bbaba58..dc75047267 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index f37139b3c5..ea568dfcd9 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 1dac54c142..5021742154 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 013a0b5c47..d30a9ca71f 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 4d34a13f38..c1c0980b64 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 0fb241c558..a29dc1ef54 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.9.0", + "version": "4.0.0", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index f747db1438..62545f5ad6 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 3a4834236c..1b03765586 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index d77384cc74..ff394295f4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 11295dc4b9..6142c3c5d8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/docker/install-tb.sh b/msa/tb/docker/install-tb.sh index 9567908316..b926aac0bb 100644 --- a/msa/tb/docker/install-tb.sh +++ b/msa/tb/docker/install-tb.sh @@ -40,7 +40,6 @@ fi CONF_FOLDER="${pkg.installFolder}/conf" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf -upgradeversion=${DATA_FOLDER}/.upgradeversion source "${CONF_FOLDER}/${configfile}" @@ -54,5 +53,3 @@ java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardI -Dinstall.upgrade=false \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher - -echo "${pkg.upgradeVersion}" > ${upgradeversion} diff --git a/msa/tb/docker/upgrade-tb.sh b/msa/tb/docker/upgrade-tb.sh index 545bf4b8d6..3c8e4f194d 100644 --- a/msa/tb/docker/upgrade-tb.sh +++ b/msa/tb/docker/upgrade-tb.sh @@ -20,28 +20,15 @@ start-db.sh CONF_FOLDER="${pkg.installFolder}/conf" jarfile=${pkg.installFolder}/bin/${pkg.name}.jar configfile=${pkg.name}.conf -upgradeversion=${DATA_FOLDER}/.upgradeversion source "${CONF_FOLDER}/${configfile}" -FROM_VERSION=`cat ${upgradeversion}` - echo "Starting ThingsBoard upgrade ..." -if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi - java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ -Dinstall.upgrade=true \ - -Dinstall.upgrade.from_version=${fromVersion} \ -Dlogging.config=/usr/share/thingsboard/bin/install/logback.xml \ org.springframework.boot.loader.launch.PropertiesLauncher -echo "${pkg.upgradeVersion}" > ${upgradeversion} - stop-db.sh \ No newline at end of file diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 79a2a66385..0c6046c09e 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa @@ -38,7 +38,6 @@ tb-postgres tb-cassandra /usr/share/${pkg.name} - 3.9.0 diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 9ec8589d9a..325d4d45fa 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index d562f89cd5..945c523455 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 992187316a..4d0370c1cf 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 32aa6a4f23..20791ad61c 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 34be0bd338..0b92e1fa97 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 95919ad175..b16469523e 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 8aeeed2b1e..72995b05ab 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 0932bf7c06..1b88a41692 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 84b1a4a488..3374112096 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.9.0", + "version": "4.0.0", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 195ae123f4..361487b516 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 7de766b2da..f7361a686d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard netty-mqtt - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 8dc3e1c40a..0135a1b4ea 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 069f7b84f0..c8232512f7 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index e6abae70ef..bd14daf22e 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 57bc5771d2..886a2169f3 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 99853b0950..750701894f 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 2d196cec7d..4f7c3f48a6 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 76655178ae..452ef6ec9a 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 7e569ea1f1..2408340792 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index b5b7867146..3ba43f5b53 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 88009474aa..e31d618890 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 8e7f77be9b..dd1c415e6c 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 02fe36f657..08f23d2c2d 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 0eff3bbd8d..d8870a3568 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "3.9.0", + "version": "4.0.0", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index f16e352d71..9515f3b3ec 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-SNAPSHOT + 4.0.0-SNAPSHOT thingsboard org.thingsboard From 190bba72f9916d1a93022206702b485f8f90f610 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:52:14 +0200 Subject: [PATCH 024/103] Temp Version set to 3.9.0-RC --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 112cc82ff7..ef57037448 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 9b0053e0b6..cbbce12336 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index fd9802a689..a4f7dd4f7b 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index d53ac4904d..4bbe65d99f 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 30001c929d..32da47ca08 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 0bd0f476ec..7d48a63903 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index f7c559c728..e6eca1aa93 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 4f0fe5aac0..68e647642d 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 0e290d1e01..0614c94bc4 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 4c8f56cbce..8bd1904d72 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 9a9b262d82..fded7298e4 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index 6fad5efd5a..fab4ff3989 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 8c4ea360cf..1aad1dd39e 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 2b780bf489..38a14cd8f3 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index ed93701513..4210f1f528 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 7cc67d5821..231811144a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 86459e9821..0bd4c0d6e3 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 8ab2243ceb..5a0dd9e4ff 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index a6e7209f21..ddb9d7a399 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index d074afc6b5..45d228f225 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index fdd3ad21d5..6c4310151d 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 5d4d37547f..a2b9dd7c84 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index e2f03b060a..8f01cab08b 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index dc75047267..194724b70f 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index ea568dfcd9..8e3fceff99 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 5021742154..c1f5a1f62b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index d30a9ca71f..e19acb0fe6 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index c1c0980b64..5563fcc8d8 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 62545f5ad6..daea52bfa5 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 1b03765586..20f8d90695 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa diff --git a/msa/pom.xml b/msa/pom.xml index ff394295f4..057f37a575 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 6142c3c5d8..3ca22bcf55 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 0c6046c09e..5e79740c8b 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 325d4d45fa..089581f1cb 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 945c523455..4d9e17df5f 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 4d0370c1cf..94b86d035f 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 20791ad61c..2b637ddd19 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 0b92e1fa97..4f7e0ad030 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index b16469523e..94bc03f29d 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 4.0.0-SNAPSHOT + 3.9.0-RC org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 72995b05ab..f6dc43ea13 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 1b88a41692..725c3e40ad 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 361487b516..c5a900e004 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index f7361a686d..d4cd55629d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard netty-mqtt - 4.0.0-SNAPSHOT + 3.9.0-RC jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 0135a1b4ea..094c48fd5d 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index c8232512f7..f868acabf5 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index bd14daf22e..fbefcd1de9 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 886a2169f3..3ee6496452 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 750701894f..33bb15af00 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 4f7c3f48a6..dce8e1606d 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 452ef6ec9a..eef23eae9b 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 2408340792..d841c56da7 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index 3ba43f5b53..cec30fcbc5 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index e31d618890..edf3727320 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index dd1c415e6c..d744f7eb2f 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 08f23d2c2d..dcd08c10af 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 9515f3b3ec..1384b1c3e6 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 4.0.0-SNAPSHOT + 3.9.0-RC thingsboard org.thingsboard From 77d2826f9637f791efff9b127fab3dc24549ffe5 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 4 Dec 2024 16:53:19 +0200 Subject: [PATCH 025/103] Version set to 4.0.0-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 56 files changed, 57 insertions(+), 57 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index ef57037448..112cc82ff7 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index cbbce12336..9b0053e0b6 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index a4f7dd4f7b..fd9802a689 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index 4bbe65d99f..d53ac4904d 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 32da47ca08..30001c929d 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 7d48a63903..0bd0f476ec 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index e6eca1aa93..f7c559c728 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 68e647642d..4f0fe5aac0 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 0614c94bc4..0e290d1e01 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 8bd1904d72..4c8f56cbce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index fded7298e4..9a9b262d82 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index fab4ff3989..6fad5efd5a 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index 1aad1dd39e..8c4ea360cf 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 38a14cd8f3..2b780bf489 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index 4210f1f528..ed93701513 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 231811144a..7cc67d5821 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 0bd4c0d6e3..86459e9821 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 5a0dd9e4ff..8ab2243ceb 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index ddb9d7a399..a6e7209f21 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 45d228f225..d074afc6b5 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 6c4310151d..fdd3ad21d5 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index a2b9dd7c84..5d4d37547f 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 8f01cab08b..e2f03b060a 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 194724b70f..dc75047267 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index 8e3fceff99..ea568dfcd9 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index c1f5a1f62b..5021742154 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index e19acb0fe6..d30a9ca71f 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 5563fcc8d8..c1c0980b64 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index daea52bfa5..62545f5ad6 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 20f8d90695..1b03765586 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 057f37a575..ff394295f4 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 3ca22bcf55..6142c3c5d8 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 5e79740c8b..0c6046c09e 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 089581f1cb..325d4d45fa 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 4d9e17df5f..945c523455 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 94b86d035f..4d0370c1cf 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 2b637ddd19..20791ad61c 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 4f7e0ad030..0b92e1fa97 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 94bc03f29d..b16469523e 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.9.0-RC + 4.0.0-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index f6dc43ea13..72995b05ab 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 725c3e40ad..1b88a41692 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index c5a900e004..361487b516 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index d4cd55629d..f7361a686d 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard netty-mqtt - 3.9.0-RC + 4.0.0-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 094c48fd5d..0135a1b4ea 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index f868acabf5..c8232512f7 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index fbefcd1de9..bd14daf22e 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 3ee6496452..886a2169f3 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 33bb15af00..750701894f 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index dce8e1606d..4f7c3f48a6 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index eef23eae9b..452ef6ec9a 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index d841c56da7..2408340792 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index cec30fcbc5..3ba43f5b53 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index edf3727320..e31d618890 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index d744f7eb2f..dd1c415e6c 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index dcd08c10af..08f23d2c2d 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 1384b1c3e6..9515f3b3ec 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.9.0-RC + 4.0.0-SNAPSHOT thingsboard org.thingsboard From 9fc597a5a4d933da7728a21a8d49cf893c93bc37 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 4 Dec 2024 18:22:00 +0200 Subject: [PATCH 026/103] fixed oauth2 client fetch by for mobile apps --- .../dao/oauth2/OAuth2ClientServiceImpl.java | 2 +- .../dao/sql/oauth2/JpaOAuth2ClientDao.java | 14 ++++++++++++-- .../dao/sql/oauth2/OAuth2ClientRepository.java | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java index 683c323ab5..452bd7ccf7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java @@ -64,7 +64,7 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA @Override public List findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(String pkgName, PlatformType platformType) { - log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]",pkgName, platformType); + log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]", pkgName, platformType); return oauth2ClientDao.findEnabledByPkgNameAndPlatformType(pkgName, platformType) .stream() .map(OAuth2Utils::toClientLoginInfo) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java index 4310e0b705..31464cbe9a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientDao.java @@ -31,6 +31,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ClientDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -65,8 +66,17 @@ public class JpaOAuth2ClientDao extends JpaAbstractDao findEnabledByPkgNameAndPlatformType(String pkgName, PlatformType platformType) { - return DaoUtil.convertDataList(repository.findEnabledByPkgNameAndPlatformType(pkgName, - platformType != null ? platformType.name() : null)); + List clientEntities; + if (platformType != null) { + clientEntities = switch (platformType) { + case ANDROID -> repository.findEnabledByAndroidPkgNameAndPlatformType(pkgName, platformType.name()); + case IOS -> repository.findEnabledByIosPkgNameAndPlatformType(pkgName, platformType.name()); + default -> Collections.emptyList(); + }; + } else { + clientEntities = Collections.emptyList(); + } + return DaoUtil.convertDataList(clientEntities); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java index f7fe5bce3a..2a5526c146 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRepository.java @@ -49,11 +49,21 @@ public interface OAuth2ClientRepository extends JpaRepository findEnabledByPkgNameAndPlatformType(@Param("pkgName") String pkgName, - @Param("platformFilter") String platformFilter); + List findEnabledByAndroidPkgNameAndPlatformType(@Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); + + @Query("SELECT c " + + "FROM OAuth2ClientEntity c " + + "LEFT JOIN MobileAppBundleOauth2ClientEntity ac ON c.id = ac.oauth2ClientId " + + "LEFT JOIN MobileAppBundleEntity b ON ac.mobileAppBundleId = b.id " + + "LEFT JOIN MobileAppEntity iosApp ON b.iosAppID = iosApp.id " + + "WHERE iosApp.pkgName = :pkgName AND b.oauth2Enabled = true " + + "AND (:platformFilter IS NULL OR c.platforms IS NULL OR c.platforms = '' OR ilike(c.platforms, CONCAT('%', :platformFilter, '%')) = true)") + List findEnabledByIosPkgNameAndPlatformType(@Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); @Query("SELECT c " + "FROM OAuth2ClientEntity c " + From b2c934a4a44f450573a4f83b1a4d5e64a60a239a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 5 Dec 2024 10:57:29 +0200 Subject: [PATCH 027/103] Fixed ordering for available notification delivery methods --- .../thingsboard/server/controller/NotificationController.java | 2 +- .../service/notification/DefaultNotificationCenter.java | 4 ++-- .../org/thingsboard/rule/engine/api/NotificationCenter.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index e055c6d6ae..72c339c635 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -477,7 +477,7 @@ public class NotificationController extends BaseController { SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/notification/deliveryMethods") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + public List getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { return notificationCenter.getAvailableDeliveryMethods(user.getTenantId()); } 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 311fe6cb2c..af63256f0f 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 @@ -417,7 +417,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } @Override - public Set getAvailableDeliveryMethods(TenantId tenantId) { + public List getAvailableDeliveryMethods(TenantId tenantId) { return channels.values().stream() .filter(channel -> { try { @@ -428,7 +428,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } }) .map(NotificationChannel::getDeliveryMethod) - .collect(Collectors.toSet()); + .sorted().toList(); } @Override diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java index 02ee53f3ad..b8fb9b61ff 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; -import java.util.Set; +import java.util.List; public interface NotificationCenter { @@ -48,6 +48,6 @@ public interface NotificationCenter { void deleteNotification(TenantId tenantId, UserId recipientId, NotificationId notificationId); - Set getAvailableDeliveryMethods(TenantId tenantId); + List getAvailableDeliveryMethods(TenantId tenantId); } From 143d5a95b33e090d932747db72edf024285e2ebc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 5 Dec 2024 11:23:51 +0200 Subject: [PATCH 028/103] UI: Fixed double open dialog when edit mobile bundle --- .../mobile-bundle-table-config.resolve.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts index 24a503147e..177f41ba1b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts @@ -24,13 +24,13 @@ import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { MobileAppBundleInfo } from '@shared/models/mobile-app.models'; -import { ActivatedRouteSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { Direction } from '@shared/models/page/sort-order'; import { MobileBundleTableHeaderComponent } from '@home/pages/mobile/bundes/mobile-bundle-table-header.component'; import { DatePipe } from '@angular/common'; import { MobileAppService } from '@core/http/mobile-app.service'; -import { map, take } from 'rxjs/operators'; +import { finalize, map, skip, take, takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { MatDialog } from '@angular/material/dialog'; @@ -52,11 +52,14 @@ export class MobileBundleTableConfigResolver { private readonly config: EntityTableConfig = new EntityTableConfig(); + private openingEditDialog = false; + constructor( private datePipe: DatePipe, private mobileAppService: MobileAppService, private translate : TranslateService, private dialog: MatDialog, + private router: Router, private store: Store ) { this.config.selectionEnabled = false; @@ -108,9 +111,15 @@ export class MobileBundleTableConfigResolver { this.config.handleRowClick = ($event, bundle) => { $event?.stopPropagation(); - this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).subscribe(appBundleInfo => { - this.editBundle($event, appBundleInfo); - }) + if (!this.openingEditDialog) { + this.openingEditDialog = true; + this.mobileAppService.getMobileAppBundleInfoById(bundle.id.id).pipe( + takeUntil(this.router.events.pipe(skip(1))), + finalize(() => {this.openingEditDialog = false;}) + ).subscribe( + appBundleInfo => this.editBundle($event, appBundleInfo) + ); + } return true; }; From 9a5d37c3c229bdeb843c6dc3b39f3a61734fd3d2 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 5 Dec 2024 12:02:40 +0200 Subject: [PATCH 029/103] UI: Fixed for value less than min and more than max --- .../system/scada_symbols/dynamic-horizontal-scale-hp.svg | 8 ++++---- .../system/scada_symbols/dynamic-vertical-scale-hp.svg | 8 ++++---- .../system/scada_symbols/simple-horizontal-scale-hp.svg | 8 ++++---- .../system/scada_symbols/simple-vertical-scale-hp.svg | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index 5576c939c4..bfd711cd38 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index b7d5e0184b..5dd6dd43a3 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = Number.MAX_SAFE_INTEGER;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = Number.MIN_SAFE_INTEGER;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index e69daae548..7b43164cce 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index 7f9244eaea..8cceb84a74 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -18,12 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = Number.MAX_SAFE_INTEGER;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -33,12 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = Number.MIN_SAFE_INTEGER;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { From a2ae5b06c01ad9638c01690c3d3e0fc1c38db48d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 5 Dec 2024 12:44:50 +0200 Subject: [PATCH 030/103] Non-null check for updateResourcesUsage --- .../server/common/data/widget/WidgetType.java | 3 ++- .../server/dao/resource/BaseResourceService.java | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java index d025aab3d5..fed271cbbe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java @@ -50,7 +50,8 @@ public class WidgetType extends BaseWidgetType { @JsonIgnore public JsonNode getDefaultConfig() { - return Optional.ofNullable(descriptor.get("defaultConfig")) + return Optional.ofNullable(descriptor) + .map(descriptor -> descriptor.get("defaultConfig")) .filter(JsonNode::isTextual).map(JsonNode::asText) .map(json -> { try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 361d2b3894..9395aae6c7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -403,6 +403,9 @@ public class BaseResourceService extends AbstractCachedEntityService links = getResourcesLinks(dashboard.getResources()); return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links); } @@ -413,8 +416,10 @@ public class BaseResourceService extends AbstractCachedEntityService jsonNodes = new ArrayList<>(2); List> mappings = new ArrayList<>(2); - jsonNodes.add(widgetTypeDetails.getDescriptor()); - mappings.add(WIDGET_RESOURCES_MAPPING); + if (widgetTypeDetails.getDescriptor() != null) { + jsonNodes.add(widgetTypeDetails.getDescriptor()); + mappings.add(WIDGET_RESOURCES_MAPPING); + } JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig(); if (defaultConfig != null) { From 0af23059320768ada2d26605a947087f4a0bf822 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 5 Dec 2024 21:40:36 +0100 Subject: [PATCH 031/103] removed FROM_VERSION check from upgrade scripts --- docker/docker-upgrade-tb.sh | 8 +------- msa/tb-node/docker/start-tb-node.sh | 8 ++------ msa/tb/docker/upgrade-tb.sh | 7 +------ packaging/java/scripts/install/upgrade.sh | 8 +------- packaging/java/scripts/install/upgrade_dev_db.sh | 8 +------- packaging/java/scripts/windows/upgrade.bat | 6 ------ 6 files changed, 6 insertions(+), 39 deletions(-) diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 41f50c7019..07aa7ef05f 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: docker-upgrade-tb.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" set -e diff --git a/msa/tb-node/docker/start-tb-node.sh b/msa/tb-node/docker/start-tb-node.sh index c164954a82..b90553e772 100755 --- a/msa/tb-node/docker/start-tb-node.sh +++ b/msa/tb-node/docker/start-tb-node.sh @@ -47,12 +47,8 @@ elif [ "$UPGRADE_TB" == "true" ]; then echo "Starting ThingsBoard upgrade ..." - if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 - else - fromVersion="${FROM_VERSION// }" - fi + + fromVersion="${FROM_VERSION// }" exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ diff --git a/msa/tb/docker/upgrade-tb.sh b/msa/tb/docker/upgrade-tb.sh index 545bf4b8d6..3e8f7d804a 100644 --- a/msa/tb/docker/upgrade-tb.sh +++ b/msa/tb/docker/upgrade-tb.sh @@ -28,12 +28,7 @@ FROM_VERSION=`cat ${upgradeversion}` echo "Starting ThingsBoard upgrade ..." -if [[ -z "${FROM_VERSION// }" ]]; then - echo "FROM_VERSION variable is invalid or unspecified!" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ -Dspring.jpa.hibernate.ddl-auto=none \ diff --git a/packaging/java/scripts/install/upgrade.sh b/packaging/java/scripts/install/upgrade.sh index 2b87b26aa6..07164ef2c8 100755 --- a/packaging/java/scripts/install/upgrade.sh +++ b/packaging/java/scripts/install/upgrade.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" CONF_FOLDER=${pkg.installFolder}/conf configfile=${pkg.name}.conf diff --git a/packaging/java/scripts/install/upgrade_dev_db.sh b/packaging/java/scripts/install/upgrade_dev_db.sh index d0c42eaaa6..010f6d3655 100755 --- a/packaging/java/scripts/install/upgrade_dev_db.sh +++ b/packaging/java/scripts/install/upgrade_dev_db.sh @@ -28,13 +28,7 @@ case $i in esac done -if [[ -z "${FROM_VERSION// }" ]]; then - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade_dev_db.sh --fromVersion={VERSION}" - exit 1 -else - fromVersion="${FROM_VERSION// }" -fi +fromVersion="${FROM_VERSION// }" BASE=${project.basedir}/target CONF_FOLDER=${BASE}/conf diff --git a/packaging/java/scripts/windows/upgrade.bat b/packaging/java/scripts/windows/upgrade.bat index b86121b8f1..115eea5244 100644 --- a/packaging/java/scripts/windows/upgrade.bat +++ b/packaging/java/scripts/windows/upgrade.bat @@ -15,12 +15,6 @@ IF NOT "%1"=="" ( GOTO :loop ) -if not defined fromVersion ( - echo "--fromVersion parameter is invalid or unspecified!" - echo "Usage: upgrade.bat --fromVersion {VERSION}" - exit /b 1 -) - SET LOADER_PATH=%BASE%\conf,%BASE%\extensions SET SQL_DATA_FOLDER=%BASE%\data\sql SET jarfile=%BASE%\lib\${pkg.name}.jar From 48c8160e1f01cab7de5c0e6a7315bf85805f6ec5 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 09:54:03 +0200 Subject: [PATCH 032/103] UI: Add no text for resources autocomplete --- .../resource/resource-autocomplete.component.html | 13 +++++++++++-- .../resource/resource-autocomplete.component.ts | 8 +++++++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 +++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index 7d0c9ffb9f..bcc566aeea 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -41,8 +41,17 @@ - - {{ searchText }} + +
+
+ {{ 'js-func.no-js-module-text' | translate }} +
+ + + {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} + + +
diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index 0cf13f2052..b87cfe8c4f 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -33,6 +33,7 @@ import { TbResourceId } from '@shared/models/id/tb-resource-id'; import { ResourceService } from '@core/http/resource.service'; import { PageLink } from '@shared/models/page/page-link'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-resource-autocomplete', @@ -95,7 +96,8 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn private propagateChange: (value: any) => void = () => {}; constructor(private fb: FormBuilder, - private resourceService: ResourceService) { + private resourceService: ResourceService, + public translate: TranslateService) { } ngOnInit(): void { @@ -210,6 +212,10 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn } } + textIsNotEmpty(text: string): boolean { + return (text && text.length > 0); + } + private fetchResources(searchText?: string): Observable> { this.searchText = searchText; return this.resourceService.getResources(new PageLink(50, 0, searchText), ResourceType.JS_MODULE, this.subType, {ignoreLoading: true}).pipe( 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 c2f45da84f..6b50deb6ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3332,7 +3332,9 @@ "module-no-members": "Module has no exported members", "module-load-error": "Module load error", "source-code": "Source code", - "source-code-load-error": "Source code load error" + "source-code-load-error": "Source code load error", + "no-js-module-text": "No JS module found", + "no-js-module-matching": "No JS module matching '{{module}}' were found." }, "key-val": { "key": "Key", From 97f3288c893e6395a21b0a89033ccf6c99d2553d Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 11:25:06 +0200 Subject: [PATCH 033/103] UI: Fixed hide menu export dashboard after select option --- .../home/components/dashboard-page/dashboard-page.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e22975f7e..4a213ce263 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 @@ -874,7 +874,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC public exportDashboard($event: Event) { if ($event) { - $event.stopPropagation(); + $event.preventDefault(); } this.importExport.exportDashboard(this.currentDashboardId); } From 76c39b6d0b2824aa66bf2bc10df8a066a5333e25 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 12:14:06 +0200 Subject: [PATCH 034/103] UI: Fixed error toast when editing device detais --- .../ota-package/ota-package-autocomplete.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 04511fad38..d3570150e8 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,10 +31,11 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL } from '@core/utils'; +import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; @Component({ selector: 'tb-ota-package-autocomplete', @@ -76,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value; + this.deviceProfile = value ? value :NULL_UUID; } } From 3a97faeb17447d2ac149ba607e8dd41b7babb119 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 6 Dec 2024 12:21:29 +0200 Subject: [PATCH 035/103] UI: Refactoring --- .../ota-package/ota-package-autocomplete.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index d3570150e8..4a117d74b9 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,7 +31,7 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; +import { getEntityDetailsPageURL } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; @@ -77,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value ? value :NULL_UUID; + this.deviceProfile = value ? value : NULL_UUID; } } From d56fc4c440edc518979919a74f9b1caeb1bb0e5c Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Dec 2024 12:35:14 +0200 Subject: [PATCH 036/103] UI: Fixed mobile center translate, validation and editor --- .../mobile/applications/mobile-app.component.ts | 8 ++++---- .../mobile/bundes/layout/mobile-layout.component.ts | 2 +- .../bundes/layout/mobile-page-item-row.component.ts | 4 ++-- .../pages/mobile/common/editor-panel.component.ts | 12 ++++++++++-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts index 803748c7d5..0d7a9c081c 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app.component.ts @@ -76,9 +76,9 @@ export class MobileAppComponent extends EntityComponent { }), storeInfo: this.fb.group({ storeLink: [entity?.storeInfo?.storeLink ? entity.storeInfo.storeLink : '', - Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)], + Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)], sha256CertFingerprints: [entity?.storeInfo?.sha256CertFingerprints ? entity.storeInfo.sha256CertFingerprints : '', - Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){1,31}$/)], + Validators.pattern(/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){31}$/)], appId: [entity?.storeInfo?.appId ? entity.storeInfo.appId : '', Validators.pattern(/^[A-Z0-9]{10}\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/)], }), }); @@ -89,11 +89,11 @@ export class MobileAppComponent extends EntityComponent { if (value === PlatformType.ANDROID) { form.get('storeInfo.sha256CertFingerprints').enable({emitEvent: false}); form.get('storeInfo.appId').disable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/play\.google\.com\/store\/apps\/details\?id=[a-zA-Z0-9._]+(?:&[a-zA-Z0-9._-]+=[a-zA-Z0-9._%-]*)*$/)); } else if (value === PlatformType.IOS) { form.get('storeInfo.sha256CertFingerprints').disable({emitEvent: false}); form.get('storeInfo.appId').enable({emitEvent: false}); - form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}$/)); + form.get('storeInfo.storeLink').setValidators(Validators.pattern(/^https?:\/\/apps\.apple\.com\/[a-z]{2}\/app\/[\w-]+\/id\d{7,10}(?:\?[^\s]*)?$/)); } form.get('storeInfo.storeLink').setValue('', {emitEvent: false}); }); diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts index 2b561a4fce..c7dd980378 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-layout.component.ts @@ -207,7 +207,7 @@ export class MobileLayoutComponent implements ControlValueAccessor, Validator { private updateModel() { if (isDefaultMobilePagesConfig(this.pagesForm.value.pages as MobilePage[])) { - this.propagateChange({pages: []}); + this.propagateChange(null); } else { this.propagateChange(this.pagesForm.value); } diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index eb9f7a2d63..9a60c87959 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -187,7 +187,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, } } else { this.isCustomMenuItem = true; - this.mobilePageRowForm.get('label').setValidators([Validators.required]); + this.mobilePageRowForm.get('label').addValidators([Validators.required]); this.mobilePageRowForm.get('label').updateValueAndValidity({emitEvent: false}); } this.updateCleanupState(); @@ -270,7 +270,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, private updateModel() { this.modelValue.visible = this.mobilePageRowForm.get('visible').value; - const label = this.mobilePageRowForm.get('label').value; + const label = this.mobilePageRowForm.get('label').value.trim(); if (label) { this.modelValue.label = label; } else { diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 9b9b282e9d..15dff508c8 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -17,6 +17,7 @@ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { FormBuilder, FormControl } from '@angular/forms'; import { TbPopoverComponent } from '@shared/components/popover.component'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-release-notes-panel', @@ -43,7 +44,7 @@ export class EditorPanelComponent implements OnInit { editorControl: FormControl; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['lists'], @@ -55,7 +56,14 @@ export class EditorPanelComponent implements OnInit { autofocus: false, branding: false, promotion: false, - resize: false + resize: false, + setup: (editor) => { + editor.on('PostRender', function() { + const container = editor.getContainer(); + const uiContainer = document.querySelector('.tox.tox-tinymce-aux'); + container.parentNode.appendChild(uiContainer); + }); + } }; constructor(private fb: FormBuilder) { 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 c2f45da84f..491e7136a6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3550,7 +3550,7 @@ "tablet-959": "Tablet (max 959px)", "max-element-number": "Max elements number", "page-name": "Page name", - "page-nam-required": "Page name is required.", + "page-name-required": "Page name is required.", "page-type": "Page type", "pages-types": { "dashboard": "Dashboard", @@ -3560,7 +3560,7 @@ "url": "URL", "url-pattern": "Invalid URL", "path": "Path", - "path-pattern": "Path pattern", + "path-pattern": "Invalid path", "custom-page": "Custom page", "edit-page": "Edit page", "edit-custom-page": "Edit custom page", From 617381ffc76b12304eb7a4e818f3abedf08c16d0 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 6 Dec 2024 13:05:53 +0200 Subject: [PATCH 037/103] UI: Fix Json Form conditions. --- .../components/json-form/react/json-form-schema-form.tsx | 6 +++--- .../shared/components/json-form/react/json-form.models.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx index 5ce35ae96f..bbdd90cbe7 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx @@ -131,10 +131,10 @@ class ThingsboardSchemaForm extends React.Component { } if (form.condition) { this.hasConditions = true; - if (!this.conditionFunction) { - this.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`); + if (!form.conditionFunction) { + form.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`); } - if (this.conditionFunction(form, model, index) === false) { + if (form.conditionFunction(form, model, index) === false) { return null; } } diff --git a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts index edba015521..ffcf201136 100644 --- a/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts +++ b/ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts @@ -87,6 +87,7 @@ export interface JsonFormData { required: boolean; default?: any; condition?: string; + conditionFunction?: Function; style?: any; rows?: number; rowsMax?: number; From e563649e98e2c4891ef404f7c1f1e3464b253f62 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Dec 2024 13:09:59 +0200 Subject: [PATCH 038/103] UI: Fixed load background in image map --- .../home/components/widget/lib/maps/providers/image-map.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts index 0abcc9f7c8..d91a84d3b2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts @@ -23,14 +23,14 @@ import { PosFunction, WidgetUnitedMapSettings } from '../map-models'; -import { forkJoin, Observable, of, ReplaySubject, switchMap } from 'rxjs'; +import { combineLatest, Observable, of, ReplaySubject, switchMap } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { calculateNewPointCoordinate, loadImageWithAspect } from '@home/components/widget/lib/maps/common-maps-utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { DataSet, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseFunction, parseTbFunction } from '@core/utils'; +import { isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, parseTbFunction } from '@core/utils'; import { EntityDataPageLink } from '@shared/models/query/query.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { CompiledTbFunction } from '@shared/models/js-function.models'; @@ -55,7 +55,7 @@ export class ImageMap extends LeafletMap { mapImage: this.mapImage(options) }; - forkJoin(initData).subscribe(inited => { + combineLatest(initData).subscribe(inited => { this.posFunction = inited.posFunction; const mapImage = inited.mapImage; this.imageUrl = mapImage.imageUrl; From 2a1910792939a6224735f1e45e6c359bc2e57579 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 14:43:29 +0200 Subject: [PATCH 039/103] fixed primary key name --- dao/src/main/resources/sql/schema-entities.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f43ea4a5f6..916a5487f8 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -896,7 +896,7 @@ CREATE TABLE IF NOT EXISTS queue_stats ( ); CREATE TABLE IF NOT EXISTS qr_code_settings ( - id uuid NOT NULL CONSTRAINT mobile_app_settings_pkey PRIMARY KEY, + id uuid NOT NULL CONSTRAINT qr_code_settings_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, use_default_app boolean, From 534e2349fbc264e941476bfc41ac87c4e8be2117 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 14:49:15 +0200 Subject: [PATCH 040/103] updated upgrade script to rename old primary key name --- application/src/main/data/upgrade/basic/schema_update.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index 9390238139..ced110e65a 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -189,6 +189,7 @@ $$ END IF; END LOOP; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key; + ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey; END IF; ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config; END; From 8e20d740338f27825a2c2a92a03b93f363b1f5a5 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 6 Dec 2024 16:54:18 +0200 Subject: [PATCH 041/103] Share echarts-widget.models --- ui-ngx/src/app/modules/home/components/public-api.ts | 1 + 1 file changed, 1 insertion(+) 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 461c106b51..6fa57fb4c4 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -19,6 +19,7 @@ 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/lib/chart/echarts-widget.models'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; From 09bc214bd3faa487e11e9952fde7b21a6c488a4d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 17:07:15 +0200 Subject: [PATCH 042/103] UI: Debug Settings fixes --- .../app/core/services/item-buffer.service.ts | 2 +- .../debug-settings-button.component.html | 11 +++--- .../debug-settings-button.component.ts | 39 ++++++++++++------- .../debug-settings-panel.component.html | 5 ++- .../debug-settings-panel.component.ts | 7 ++-- .../rulechain/rulechain-page.component.html | 6 +-- .../rulechain/rulechain-page.component.ts | 18 ++++++--- .../pipe/milliseconds-to-time-string.pipe.ts | 15 ++++--- .../assets/locale/locale.constant-ar_AE.json | 1 - .../assets/locale/locale.constant-ca_ES.json | 3 +- .../assets/locale/locale.constant-cs_CZ.json | 3 +- .../assets/locale/locale.constant-da_DK.json | 3 +- .../assets/locale/locale.constant-el_GR.json | 3 +- .../assets/locale/locale.constant-en_US.json | 7 ++-- .../assets/locale/locale.constant-es_ES.json | 1 - .../assets/locale/locale.constant-ka_GE.json | 3 +- .../assets/locale/locale.constant-ko_KR.json | 3 +- .../assets/locale/locale.constant-lt_LT.json | 2 +- .../assets/locale/locale.constant-lv_LV.json | 3 +- .../assets/locale/locale.constant-nl_BE.json | 3 +- .../assets/locale/locale.constant-pl_PL.json | 1 - .../assets/locale/locale.constant-pt_BR.json | 3 +- .../assets/locale/locale.constant-ro_RO.json | 1 - .../assets/locale/locale.constant-sl_SI.json | 3 +- .../assets/locale/locale.constant-tr_TR.json | 3 +- .../assets/locale/locale.constant-uk_UA.json | 3 +- .../assets/locale/locale.constant-zh_CN.json | 1 - .../assets/locale/locale.constant-zh_TW.json | 3 +- 28 files changed, 79 insertions(+), 77 deletions(-) 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 c77784c4f5..ff4211961a 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -305,7 +305,7 @@ export class ItemBufferService { connectors: [], additionalInfo: origNode.additionalInfo, configuration: origNode.configuration, - debugMode: origNode.debugMode, + debugSettings: origNode.debugSettings, x: origNode.x, y: origNode.y, name: origNode.name, diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html index b779ae1e4c..d20fa2e90d 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html @@ -23,10 +23,9 @@ [disabled]="disabled" (click)="openDebugStrategyPanel($event, matButton)"> bug_report - common.disabled - debug-config.all - - {{ !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }} - - debug-config.failures + @if (isDebugAllActive$ | async) { + {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() }} + } @else { + {{ failuresEnabled ? ('debug-config.failures' | translate) : ('common.disabled' | translate) }} + } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts index dafda75c0b..4db01892a2 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts @@ -14,18 +14,26 @@ /// limitations under the License. /// -import { ChangeDetectionStrategy, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + forwardRef, + Input, + Renderer2, + signal, + ViewContainerRef +} from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; -import { SECOND } from '@shared/models/time/time.models'; +import { MINUTE, SECOND } from '@shared/models/time/time.models'; import { DebugSettings } from '@shared/models/entity.models'; -import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; +import { map, switchMap, takeWhile } from 'rxjs/operators'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; import { Store } from '@ngrx/store'; @@ -60,11 +68,11 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { }); disabled = false; + allEnabled = signal(false); - isDebugAllActive$ = this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( - startWith(null), - switchMap(() => { - if (this.allEnabled) { + isDebugAllActive$ = toObservable(this.allEnabled).pipe( + switchMap((value) => { + if (value) { return of(true); } else { return timer(0, SECOND).pipe( @@ -77,7 +85,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { shareReplay(1) ); - readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; + readonly maxDebugModeDuration = getCurrentAuthState(this.store).maxDebugModeDurationMinutes * MINUTE; private propagateChange: (settings: DebugSettings) => void = () => {}; @@ -91,17 +99,17 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { takeUntilDestroyed() ).subscribe(value => { this.propagateChange(value); - }) + }); + + this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => this.allEnabled.set(value)); } get failuresEnabled(): boolean { return this.debugSettingsFormGroup.get('failuresEnabled').value; } - get allEnabled(): boolean { - return this.debugSettingsFormGroup.get('allEnabled').value; - } - get allEnabledUntil(): number { return this.debugSettingsFormGroup.get('allEnabledUntil').value; } @@ -120,7 +128,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, { ...debugSettings, - maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes, + maxDebugModeDuration: this.maxDebugModeDuration, debugLimitsConfiguration: this.debugLimitsConfiguration }, {}, @@ -141,6 +149,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { writeValue(settings: DebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); + this.allEnabled.set(settings?.allEnabled); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html index 5fe0aa2546..f088fc38ac 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html @@ -20,7 +20,7 @@
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }} + {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true).trim() } }} } @else { {{ 'debug-config.hint.main' | translate }} } @@ -35,7 +35,7 @@
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }} + {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() } }}
diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts index a9e237245c..2c98dee6c4 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts @@ -52,14 +52,14 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit @Input({ transform: booleanAttribute }) failuresEnabled = false; @Input({ transform: booleanAttribute }) allEnabled = false; @Input() allEnabledUntil = 0; - @Input() maxDebugModeDurationMinutes: number; + @Input() maxDebugModeDuration: number; @Input() debugLimitsConfiguration: string; onFailuresControl = this.fb.control(false); debugAllControl = this.fb.control(false); maxMessagesCount: string; - maxTimeFrameSec: string; + maxTimeFrameDuration: number; initialAllEnabled: boolean; isDebugAllActive$ = this.debugAllControl.valueChanges.pipe( @@ -99,7 +99,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit ngOnInit(): void { this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0]; - this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1]; + this.maxTimeFrameDuration = parseInt(this.debugLimitsConfiguration?.split(':')[1]) * SECOND; this.onFailuresControl.patchValue(this.failuresEnabled); this.debugAllControl.patchValue(this.allEnabled); this.initialAllEnabled = this.allEnabled || this.allEnabledUntil > new Date().getTime(); @@ -128,6 +128,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit onReset(): void { this.debugAllControl.patchValue(true); + this.debugAllControl.markAsDirty(); this.allEnabledUntil = 0; this.cd.markForCheck(); } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html index 5e955dc4b4..7bf85aa215 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html @@ -239,10 +239,10 @@ matTooltipPosition="above"> delete - diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts index 35b10a27b3..2d440ed28c 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts @@ -95,6 +95,7 @@ import { ComponentClusteringMode } from '@shared/models/component-descriptor.mod import { MatDrawer } from '@angular/material/sidenav'; import { HttpStatusCode } from '@angular/common/http'; import { TbContextMenuEvent } from '@shared/models/jquery-event.models'; +import { DebugSettings } from '@shared/models/entity.models'; import Timeout = NodeJS.Timeout; @Component({ @@ -1415,17 +1416,20 @@ export class RuleChainPageComponent extends PageComponent this.ruleChainCanvas.modelService.deleteSelected(); } - isDebugModeEnabled(): boolean { - const res = this.ruleChainModel.nodes.find((node) => node.debugMode); + isDebugSettingsEnabled(): boolean { + const res = this.ruleChainModel.nodes.find(({ debugSettings }) => debugSettings && this.isDebugSettingsActive(debugSettings)); return typeof res !== 'undefined'; } - resetDebugModeInAllNodes() { + resetDebugSettingsInAllNodes(): void { let changed = false; this.ruleChainModel.nodes.forEach((node) => { if (node.component.type !== RuleNodeType.INPUT) { - changed = changed || node.debugMode; - node.debugMode = false; + const nodeHasActiveDebugSettings = node.debugSettings && this.isDebugSettingsActive(node.debugSettings); + changed = changed || nodeHasActiveDebugSettings; + if (nodeHasActiveDebugSettings) { + node.debugSettings = { allEnabled: false, failuresEnabled: false, allEnabledUntil: 0 }; + } } }); if (changed) { @@ -1433,6 +1437,10 @@ export class RuleChainPageComponent extends PageComponent } } + private isDebugSettingsActive({ allEnabled = false, failuresEnabled = false, allEnabledUntil = 0 }: DebugSettings): boolean { + return allEnabled || failuresEnabled || allEnabledUntil > new Date().getTime(); + } + validate() { setTimeout(() => { this.isInvalid = false; diff --git a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts index 853890ee09..d8884992bf 100644 --- a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts @@ -16,7 +16,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; +import { DAY, HOUR, MINUTE, SECOND, YEAR } from '@shared/models/time/time.models'; @Pipe({ name: 'milliSecondsToTimeString' @@ -27,19 +27,21 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { } transform(milliSeconds: number, shortFormat = false, onlyFirstDigit = false): string { - const { days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); - return this.formatTimeString(days, hours, minutes, seconds, shortFormat, onlyFirstDigit); + const { years, days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); + return this.formatTimeString(years, days, hours, minutes, seconds, shortFormat, onlyFirstDigit); } - private extractTimeUnits(milliseconds: number): { days: number; hours: number; minutes: number; seconds: number } { - const days = Math.floor(milliseconds / DAY); + private extractTimeUnits(milliseconds: number): { years: number; days: number; hours: number; minutes: number; seconds: number } { + const years = Math.floor(milliseconds / YEAR); + const days = Math.floor((milliseconds % YEAR) / DAY); const hours = Math.floor((milliseconds % DAY) / HOUR); const minutes = Math.floor((milliseconds % HOUR) / MINUTE); const seconds = Math.floor((milliseconds % MINUTE) / SECOND); - return { days, hours, minutes, seconds }; + return { years, days, hours, minutes, seconds }; } private formatTimeString( + years: number, days: number, hours: number, minutes: number, @@ -48,6 +50,7 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { onlyFirstDigit: boolean ): string { const timeUnits = [ + { value: years, key: 'years', shortKey: 'short.years' }, { value: days, key: 'days', shortKey: 'short.days' }, { value: hours, key: 'hours', shortKey: 'short.hours' }, { value: minutes, key: 'minutes', shortKey: 'short.minutes' }, diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index e4c7ddc917..695e1ad441 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -4602,7 +4602,6 @@ "output": "الإخراج", "test": "اختبار", "help": "مساعدة", - "reset-debug-mode": "إعادة تعيين وضع التصحيح في جميع العقد", "test-with-this-message": "{{test}} مع هذه الرسالة" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index c04c0e12cf..43a644a537 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -3651,8 +3651,7 @@ "metadata-required": "Les entrades de metadades no poden estar buides.", "output": "Sortida", "test": "Test", - "help": "Ajuda", - "reset-debug-mode": "Restablir el mode de depuració a tots els nodes" + "help": "Ajuda" }, "role": { "role": "Rol", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 67b8b22005..4ae54c4d65 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2426,8 +2426,7 @@ "metadata-required": "Záznam metadat nemůže být prázdný.", "output": "Výstup", "test": "Test", - "help": "Nápověda", - "reset-debug-mode": "Resetovat režim ladění na všech uzlech" + "help": "Nápověda" }, "timezone": { "timezone": "Časová zóna", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 9d08a72085..fbfeddd886 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -2642,8 +2642,7 @@ "metadata-required": "Metadataposter må ikke være tomme.", "output": "Output", "test": "Test", - "help": "Hjælp", - "reset-debug-mode": "Nulstil debug-tilstand i alle knuder" + "help": "Hjælp" }, "role": { "role": "Rolle", diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index eea0ea4377..164b5486b9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -1886,8 +1886,7 @@ "metadata-required": "Οι καταχωρίσεις μεταδεδομένων δεν μπορούν να είναι κενές.", "output": "Απόδοση", "test": "Τεστ", - "help": "Βοήθεια", - "reset-debug-mode": "Επαναφορά λειτουργίας εντοπισμού σφαλμάτων σε όλους τους κόμβους" + "help": "Βοήθεια" }, "role": { "role": "Ρόλος", 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 c2f45da84f..1968f96346 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -990,15 +990,13 @@ "type-sms-sent": "SMS sent" }, "debug-config": { - "min": "{{number}} min", "label": "Debug configuration", "on-failure": "Failures only (24/7)", "all-messages": "All messages ({{time}})", "failures": "Failures", - "all": "All", "hint": { "main": "All node debug messages rate limited with:", - "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{sec}} seconds.", + "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{time}}.", "on-failure": "Save all failure debug events without time limit.", "all-messages": "Save all debug events during time limit." } @@ -4293,7 +4291,7 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", + "reset-debug-settings": "Reset debug settings in all nodes", "test-with-this-message": "{{test}} with this message", "queue-hint": "Select a queue for message forwarding to another queue. 'Main' queue is used by default.", "queue-singleton-hint": "Select a queue for message forwarding in multi-instance environments. 'Main' queue is used by default." @@ -4743,6 +4741,7 @@ "sec": "{{ sec }} sec", "sec-short": "{{ sec }}s", "short": { + "years": "{ years, plural, =1 {1 year } other {# years } }", "days": "{ days, plural, =1 {1 day } other {# days } }", "hours": "{ hours, plural, =1 {1 hour } other {# hours } }", "minutes": "{{minutes}} min ", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 0cb9528bdd..1122c1d6fa 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3529,7 +3529,6 @@ "output": "Salida", "test": "Test", "help": "Ayuda", - "reset-debug-mode": "Restablecer el modo de depuración en todos los nodos", "test-with-this-message": "{{test}} con este mensaje" }, "timezone": { diff --git a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json index f645cb0f31..ae8ce2b943 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -1413,8 +1413,7 @@ "metadata-required": "მეტამონაცემები ვერ იქნება ცარიელი", "output": "რეზულტატი", "test": "ტესტი", - "help": "დახმარება", - "reset-debug-mode": "Debug რეჟიმის გათიშვა ყველა ნოდისთვის" + "help": "დახმარება" }, "tenant": { "tenant": "ტენანტი", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 9235fc9e95..80a2cb0830 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1878,8 +1878,7 @@ "metadata-required": "메타데이터 엔트리를 입력하세요.", "output": "출력", "test": "테스트", - "help": "도움말", - "reset-debug-mode": "모든 노드에 대해 디버그 모드 초기화" + "help": "도움말" }, "timezone": { "timezone": "Timezone", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index c856652198..1d2a672326 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -4504,7 +4504,7 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes", + "reset-debug-settings": "Reset debug settings in all nodes", "test-with-this-message": "{{test}} with this message" }, "role": { diff --git a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json index 0f5c946063..bba71609ec 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -1338,8 +1338,7 @@ "metadata-required": "Metadatu ievadi nevar būt tukši.", "output": "Izeja", "test": "Tests", - "help": "Palīdzība", - "reset-debug-mode": "Atiestatīt atkļūdošanu visās nodēs" + "help": "Palīdzība" }, "tenant": { "tenant": "Īrnieks", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index f9fe60f44d..7b99ad9953 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -4560,8 +4560,7 @@ "metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.", "output": "Uitvoer", "test": "Test", - "help": "Help", - "reset-debug-mode": "Foutopsporingsmodus resetten in alle rule nodes" + "help": "Help" }, "role": { "role": "Rol", 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 9ab89169c0..b369f061b7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -4520,7 +4520,6 @@ "output": "Wyjście", "test": "Test", "help": "Pomoc", - "reset-debug-mode": "Zresetuj tryb debugowania we wszystkich węzłach", "test-with-this-message": "{{test}} with this message", "description": "Opis" }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index 8b77be4573..bfc616e40b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1554,8 +1554,7 @@ "metadata-required": "As entradas de metadados não podem estar em branco.", "output": "Saída", "test": "Teste", - "help": "Ajuda", - "reset-debug-mode": "Redefinir modo de depuração em todos os nós" + "help": "Ajuda" }, "timezone": { "timezone": "Fuso horário", diff --git a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json index 2eb5bc491a..1054b812a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -1401,7 +1401,6 @@ "output": "Ieşire", "test": "Test", "help": "Ajutor", - "reset-debug-mode": "Dezactivează modul depanare în toate nodurile" }, "tenant": { "tenant": "Locatar", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index ff23223107..668bf8c2c9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1879,8 +1879,7 @@ "metadata-required": "Vnosi metapodatkov ne smejo biti prazni.", "output": "Izdelek", "test": "Test", - "help": "Pomoč", - "reset-debug-mode": "Ponastavi način za odpravljanje napak v vseh vozliščih" + "help": "Pomoč" }, "timezone": { "timezone": "Časovni pas", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index e954e8f2e1..98998ab312 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -2447,8 +2447,7 @@ "metadata-required": "Meta veri girişleri boş bırakılamaz.", "output": "Çıktı", "test": "Ölçek", - "help": "Yardım et", - "reset-debug-mode": "Tüm düğümlerde hata ayıklama modunu sıfırla" + "help": "Yardım et" }, "timezone": { "timezone": "Saat dilimi", diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index e1bf787994..09cf86bc21 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -1978,8 +1978,7 @@ "metadata-required": "Записи метаданих не можуть бути порожніми.", "output": "Вихід", "test": "Тест", - "help": "Допомога", - "reset-debug-mode": "Вимкнути режим налагодження у всіх правилах" + "help": "Допомога" }, "scheduler": { "scheduler": "Планувальник", 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 f0dacca36d..47096fe5f2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -4012,7 +4012,6 @@ "output": "输出", "test": "测试", "help": "帮助", - "reset-debug-mode": "重置所有节点中的调试模式", "test-with-this-message": "使用此消息进行{{test}}测试", "queue-hint": "选择一个队列将消息转发到另一个队列,默认情况下使用'Main'队列。", "queue-singleton-hint": "选择一个队列以在多实体中转发消息,默认情况下使用'Main'队列。" diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index a100d95fa4..64b4c71f16 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2768,8 +2768,7 @@ "metadata-required": "元資料項不能為空。", "output": "輸出", "test": "測試", - "help": "幫助", - "reset-debug-mode": "重置所有節點中的調試模式" + "help": "幫助" }, "timezone": { "timezone": "時區", From 327455305d661baf6257926941f598ecee82a554 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 17:48:37 +0200 Subject: [PATCH 043/103] UI: Debug Settings fixes refactoring --- .../debug-settings/debug-settings-button.component.html | 4 ++-- .../debug-settings/debug-settings-panel.component.html | 4 ++-- .../home/pages/rulechain/rulechain-page.component.ts | 8 ++++---- .../app/shared/pipe/milliseconds-to-time-string.pipe.ts | 2 +- ui-ngx/src/assets/locale/locale.constant-lt_LT.json | 1 - ui-ngx/src/assets/locale/locale.constant-uk_UA.json | 3 ++- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html index d20fa2e90d..de765556e4 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html @@ -24,8 +24,8 @@ (click)="openDebugStrategyPanel($event, matButton)"> bug_report @if (isDebugAllActive$ | async) { - {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() }} + {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} } @else { - {{ failuresEnabled ? ('debug-config.failures' | translate) : ('common.disabled' | translate) }} + {{ failuresEnabled ? 'debug-config.failures' : 'common.disabled' | translate }} } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html index f088fc38ac..d47ee69091 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html @@ -20,7 +20,7 @@
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true).trim() } }} + {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} } @else { {{ 'debug-config.hint.main' | translate }} } @@ -35,7 +35,7 @@
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true).trim() } }} + {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}
diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts index 4db01892a2..8617d25de7 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts @@ -16,6 +16,7 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, forwardRef, Input, @@ -94,6 +95,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { private store: Store, private viewContainerRef: ViewContainerRef, private fb: FormBuilder, + private cd : ChangeDetectorRef, ) { this.debugSettingsFormGroup.valueChanges.pipe( takeUntilDestroyed() @@ -136,6 +138,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { this.debugSettingsFormGroup.patchValue(settings); + this.cd.markForCheck(); debugStrategyPopover.hide(); }); } From b5dd07153725f35d728c3bd538e3c31927a5ad86 Mon Sep 17 00:00:00 2001 From: kalytka Date: Fri, 6 Dec 2024 17:56:05 +0200 Subject: [PATCH 045/103] Refactoring --- ui-ngx/src/app/modules/home/components/public-api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6fa57fb4c4..9a320664da 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -19,8 +19,8 @@ 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/lib/chart/echarts-widget.models'; +export * from './widget/lib/chart/echarts-widget.models'; export * from './widget/config/widget-config.component.models'; export * from './widget/lib/table-widget.models'; export * from './widget/lib/flot-widget.models'; From 8e34cfefc89c1fe67cfe54a4e0cf9acbbbb394b2 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 6 Dec 2024 18:44:21 +0200 Subject: [PATCH 046/103] UI: Added allEnabled = true on rule node copying if debug active --- ui-ngx/src/app/core/services/item-buffer.service.ts | 6 +++++- 1 file changed, 5 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 ff4211961a..c43d1ef35b 100644 --- a/ui-ngx/src/app/core/services/item-buffer.service.ts +++ b/ui-ngx/src/app/core/services/item-buffer.service.ts @@ -305,7 +305,11 @@ export class ItemBufferService { connectors: [], additionalInfo: origNode.additionalInfo, configuration: origNode.configuration, - debugSettings: origNode.debugSettings, + debugSettings: { + failuresEnabled: origNode.debugSettings?.failuresEnabled, + allEnabled: origNode.debugSettings?.allEnabled || origNode.debugSettings?.allEnabledUntil > new Date().getTime(), + allEnabledUntil: 0 + }, x: origNode.x, y: origNode.y, name: origNode.name, From 57df0395416e9ff2e5dd225a371f6cbd79aed8fb Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 6 Dec 2024 19:03:30 +0200 Subject: [PATCH 047/103] fixed upgrade script for disabled android/ios configuration --- .../main/data/upgrade/basic/schema_update.sql | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index ced110e65a..9aec87af00 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -143,7 +143,7 @@ $$ LOOP generatedBundleId := NULL; -- migrate android config - IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN + IF (qrCodeRecord.android_config::jsonb ->> 'appPackage' IS NOT NULL) THEN androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage'; SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID'; IF androidAppId IS NULL THEN @@ -154,17 +154,16 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId; - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId), - android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId); END IF; END IF; + UPDATE qr_code_settings SET android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; -- migrate ios config - IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN + IF (qrCodeRecord.ios_config::jsonb ->> 'appId' IS NOT NULL) THEN iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1); SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS'; IF iosAppId IS NULL THEN @@ -176,17 +175,16 @@ $$ generatedBundleId := uuid_generate_v4(); INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id) VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId); - UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId, - ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId; ELSE UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId; END IF; ELSE - UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId), - ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; + UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId); UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId; END IF; END IF; + UPDATE qr_code_settings SET ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id; END LOOP; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key; ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey; From b906f424424f90a3ca53debeb3b84e93c70b0035 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:17:25 +0200 Subject: [PATCH 048/103] UI: Refactoring --- .../ota-package/ota-package-autocomplete.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 4a117d74b9..89b728916f 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -77,7 +77,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On if (this.deviceProfile) { this.reset(); } - this.deviceProfile = value ? value : NULL_UUID; + this.deviceProfile = value ?? NULL_UUID; } } From 845f2099965b3d787fe19bfe3656fce9413151e9 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:36:51 +0200 Subject: [PATCH 049/103] UI: Refactoring no text option for extension --- .../resource-autocomplete.component.html | 25 ++++++++++++------- .../resource-autocomplete.component.ts | 2 ++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index bcc566aeea..04e7a4dbc6 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -41,17 +41,24 @@ - -
-
- {{ 'js-func.no-js-module-text' | translate }} -
- + + +
+
+ {{ 'js-func.no-js-module-text' | translate }} +
+ {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} - -
-
+
+
+
+ + + + {{ searchText }} + + diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index b87cfe8c4f..1b5a541368 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -79,6 +79,8 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn @Input() subType = ResourceSubType.EXTENSION; + ResourceSubType = ResourceSubType; + resourceFormGroup = this.fb.group({ resource: this.fb.control(null) }); From 7d34e038de53e93e36af1b0bf7270aefb86eb501 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:53:20 +0200 Subject: [PATCH 050/103] UI: translate pipe and truncate --- .../components/resource/resource-autocomplete.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html index 04e7a4dbc6..513bee2808 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.html @@ -49,7 +49,7 @@
- {{ translate.get('js-func.no-js-module-matching', {module: searchText}) | async }} + {{ 'js-func.no-js-module-matching' | translate: {module: searchText | truncate: true: 6: '...'} }}
From 928a34611ae00164a654d67f736671bf71ba0ddf Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 10:57:24 +0200 Subject: [PATCH 051/103] UI: Refactoring --- .../components/resource/resource-autocomplete.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts index 1b5a541368..3aab98a394 100644 --- a/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/resource/resource-autocomplete.component.ts @@ -33,7 +33,6 @@ import { TbResourceId } from '@shared/models/id/tb-resource-id'; import { ResourceService } from '@core/http/resource.service'; import { PageLink } from '@shared/models/page/page-link'; import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; -import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-resource-autocomplete', @@ -98,8 +97,7 @@ export class ResourceAutocompleteComponent implements ControlValueAccessor, OnIn private propagateChange: (value: any) => void = () => {}; constructor(private fb: FormBuilder, - private resourceService: ResourceService, - public translate: TranslateService) { + private resourceService: ResourceService) { } ngOnInit(): void { From 0083d92862d8d6b7ebe01a6993ddb9c8deb690b9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 9 Dec 2024 11:23:21 +0200 Subject: [PATCH 052/103] UI: Fixed relative urls in send email notification --- .../notification-template-configuration.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts index 76cc84d905..31f66f5ea2 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts @@ -37,6 +37,7 @@ import { Subject } from 'rxjs'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; import { TranslateService } from '@ngx-translate/core'; +import { EditorOptions } from 'tinymce'; @Component({ selector: 'tb-template-configuration', @@ -81,7 +82,7 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co readonly NotificationDeliveryMethod = NotificationDeliveryMethod; readonly NotificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap; - tinyMceOptions: Record = { + tinyMceOptions: Partial = { base_url: '/assets/tinymce', suffix: '.min', plugins: ['link', 'table', 'image', 'lists', 'code', 'fullscreen'], @@ -93,7 +94,8 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co height: 400, autofocus: false, branding: false, - promotion: false + promotion: false, + relative_urls: false }; private propagateChange = null; From fcb32c29f912e2a62f3a01528c4de6a500b3feb2 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 9 Dec 2024 15:08:48 +0200 Subject: [PATCH 053/103] Fix migration from postgres to kafka for edge-events --- .../service/edge/rpc/EdgeGrpcService.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) 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 71387aa542..2e9465fc9d 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 @@ -413,23 +413,27 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) { log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId()); sessionNewEvents.put(edgeId, false); - processEdgeEventMigrationIfNeeded(session, edgeId); session.processHighPriorityEvents(); - Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { - @Override - public void onSuccess(Boolean newEventsAdded) { - if (Boolean.TRUE.equals(newEventsAdded)) { - sessionNewEvents.put(edgeId, true); + processEdgeEventMigrationIfNeeded(session, edgeId); + if (Boolean.TRUE.equals(edgeEventsMigrationProcessed.get(edgeId))) { + Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { + @Override + public void onSuccess(Boolean newEventsAdded) { + if (Boolean.TRUE.equals(newEventsAdded)) { + sessionNewEvents.put(edgeId, true); + } + scheduleEdgeEventsCheck(session); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); + scheduleEdgeEventsCheck(session); } - scheduleEdgeEventsCheck(session); - } - - @Override - public void onFailure(Throwable t) { - log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t); - scheduleEdgeEventsCheck(session); - } - }, ctx.getGrpcCallbackExecutorService()); + }, ctx.getGrpcCallbackExecutorService()); + } else { + scheduleEdgeEventsCheck(session); + } } else { scheduleEdgeEventsCheck(session); } @@ -457,8 +461,6 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i scheduleEdgeEventsCheck(session); } else if (Boolean.FALSE.equals(eventsExist)) { edgeEventsMigrationProcessed.put(edgeId, true); - } else { - scheduleEdgeEventsCheck(session); } } } From d2fdcb289c61455f7a29365980ac020c9da98a1b Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 15:23:21 +0200 Subject: [PATCH 054/103] UI: Fixed dynamic sources for filters list --- .../components/filter/filter-predicate-list.component.html | 4 ++-- .../components/filter/filter-predicate-value.component.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 2e9f12da2b..b5d3489c95 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
{{ hintText | translate }}
-
+
From 6e019951cd5ed674d63c20fd6b3ee8cce6b7d58f Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 9 Dec 2024 16:00:43 +0200 Subject: [PATCH 055/103] UI: adjusted audit logs dialog codeblock height --- .../components/audit-log/audit-log-details-dialog.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts index 881961ed34..de405b0f0d 100644 --- a/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/audit-log/audit-log-details-dialog.component.ts @@ -105,7 +105,7 @@ export class AuditLogDetailsDialogComponent extends DialogComponent 0) { const lines = content.split('\n'); - newHeight = 17 * lines.length + 16; + newHeight = 18 * lines.length + 16; let maxLineLength = 0; lines.forEach((row) => { const line = row.replace(/\t/g, ' ').replace(/\n/g, ''); From d68342d883fe812e327a98960372ae44a4430143 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 16:44:00 +0200 Subject: [PATCH 056/103] UI: Refactoring inline style --- .../components/filter/filter-predicate-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index b5d3489c95..439480d5ed 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:03:55 +0200 Subject: [PATCH 057/103] Timewindow: fix applying invalid grouping interval when list of allowed intervals set --- .../src/app/shared/components/time/timeinterval.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index a7229660d5..689e42c747 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -191,8 +191,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor, OnCh if (typeof this.modelValue !== 'undefined') { const min = this.timeService.boundMinInterval(this.minValue); const max = this.timeService.boundMaxInterval(this.maxValue); - if (this.allowedIntervals?.length || - IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) { + if (IntervalMath.numberValue(this.modelValue) >= min && IntervalMath.numberValue(this.modelValue) <= max) { const advanced = this.allowedIntervals?.length ? !this.allowedIntervals.includes(this.modelValue) : !this.timeService.matchesExistingInterval(this.minValue, this.maxValue, this.modelValue, From 910e0cabb88c6eacf726d71fdc2c9d914e691e75 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 17:20:42 +0200 Subject: [PATCH 058/103] UI: Refactoring --- .../components/filter/filter-predicate-list.component.html | 4 ++-- ui-ngx/tailwind.config.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 439480d5ed..93fd973753 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -40,13 +40,13 @@
-
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:25:29 +0200 Subject: [PATCH 059/103] UI: Refactoring --- .../home/components/filter/filter-predicate-list.component.html | 2 +- ui-ngx/tailwind.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 93fd973753..7285c41a9d 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -46,7 +46,7 @@
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 17:26:25 +0200 Subject: [PATCH 060/103] UI: Refactoring --- .../home/components/filter/filter-predicate-list.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 7285c41a9d..fd1cf2ea27 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -46,7 +46,7 @@
{{ complexOperationTranslations.get(operation) | translate }}
-
+
Date: Mon, 9 Dec 2024 18:00:34 +0200 Subject: [PATCH 061/103] Revert "Added fix for timeseries chart showing hidden yAxis" This reverts commit 1bea76a774eea6c42f606d70b1c6ee741310739d. --- .../widget/lib/chart/time-series-chart-widget.component.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts index efb25a0258..ed023c1a0d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts @@ -112,9 +112,6 @@ export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterV legendKey.dataKey.settings = mergeDeep({} as TimeSeriesChartKeySettings, timeSeriesChartKeyDefaultSettings, legendKey.dataKey.settings); legendKey.dataKey.hidden = legendKey.dataKey.settings.dataHiddenByDefault; - if (this.settings.yAxes[legendKey.dataKey.settings.yAxisId]) { - this.settings.yAxes[legendKey.dataKey.settings.yAxisId].show = !legendKey.dataKey.settings.dataHiddenByDefault; - } }); this.legendKeys = this.legendKeys.filter(legendKey => legendKey.dataKey.settings.showInLegend); if (!this.legendKeys.length) { From 5b2eb75c92f56106bced631a1d7328663e122d74 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 9 Dec 2024 18:25:14 +0200 Subject: [PATCH 062/103] Update locale.constant-en_US.json --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6b50deb6ab..362df078ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3333,8 +3333,8 @@ "module-load-error": "Module load error", "source-code": "Source code", "source-code-load-error": "Source code load error", - "no-js-module-text": "No JS module found", - "no-js-module-matching": "No JS module matching '{{module}}' were found." + "no-js-module-text": "No JS modules found", + "no-js-module-matching": "No JS modules matching '{{module}}' were found." }, "key-val": { "key": "Key", From bc44779a96f8a9d8ef3284cb8e73663e56ed0782 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Mon, 9 Dec 2024 18:34:01 +0200 Subject: [PATCH 063/103] UI: Fixed scroll on progress bar for usage info widget --- .../widget/lib/home-page/usage-info-widget.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index b7c02acef6..529a3d4495 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -97,6 +97,7 @@ :host ::ng-deep { .tb-usage-items-progress { .mat-mdc-progress-bar { + overflow: hidden; .mdc-linear-progress__bar-inner { border-top-width: 8px; } From 3e2ebcf6858894c2b26dd568f105ea9e15728dcd Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 09:52:49 +0200 Subject: [PATCH 064/103] UI: Fixed tenant profile default configuration --- ...lt-tenant-profile-configuration.component.ts | 2 +- ui-ngx/src/app/shared/models/tenant.model.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) 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 490bdc902b..36ac40ecf2 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 @@ -86,7 +86,6 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA tenantNotificationRequestsRateLimit: [null, []], tenantNotificationRequestsPerRuleRateLimit: [null, []], maxTransportMessages: [null, [Validators.required, Validators.min(0)]], - maxDebugModeDurationMinutes: [null, [Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]], @@ -97,6 +96,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxSms: [null, []], smsEnabled: [null, []], maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], + maxDebugModeDurationMinutes: [null, [Validators.min(0)]], defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], alarmsTtlDays: [null, [Validators.required, Validators.min(0)]], rpcTtlDays: [null, [Validators.required, Validators.min(0)]], diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 0520dd2b4e..aefc85a7f6 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -31,6 +31,7 @@ export interface DefaultTenantProfileConfiguration { maxUsers: number; maxDashboards: number; maxRuleChains: number; + maxEdges: number; maxResourcesInBytes: number; maxOtaPackagesInBytes: number; maxResourceSize: number; @@ -42,6 +43,13 @@ export interface DefaultTenantProfileConfiguration { transportDeviceTelemetryMsgRateLimit?: string; transportDeviceTelemetryDataPointsRateLimit?: string; + transportGatewayMsgRateLimit?: string; + transportGatewayTelemetryMsgRateLimit?: string; + transportGatewayTelemetryDataPointsRateLimit?: string; + transportGatewayDeviceMsgRateLimit?: string; + transportGatewayDeviceTelemetryMsgRateLimit?: string; + transportGatewayDeviceTelemetryDataPointsRateLimit?: string; + tenantEntityExportRateLimit?: string; tenantEntityImportRateLimit?: string; tenantNotificationRequestsRateLimit?: string; @@ -59,6 +67,8 @@ export interface DefaultTenantProfileConfiguration { smsEnabled: boolean; maxCreatedAlarms: number; + maxDebugModeDurationMinutes: number; + tenantServerRestLimitsConfiguration: string; customerServerRestLimitsConfiguration: string; @@ -75,6 +85,11 @@ export interface DefaultTenantProfileConfiguration { cassandraQueryTenantRateLimitsConfiguration: string; + edgeEventRateLimits?: string; + edgeEventRateLimitsPerEdge?: string; + edgeUplinkMessagesRateLimits?: string; + edgeUplinkMessagesRateLimitsPerEdge?: string; + defaultStorageTtlDays: number; alarmsTtlDays: number; rpcTtlDays: number; @@ -100,6 +115,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxUsers: 0, maxDashboards: 0, maxRuleChains: 0, + maxEdges: 0, maxResourcesInBytes: 0, maxOtaPackagesInBytes: 0, maxResourceSize: 0, @@ -114,6 +130,7 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxSms: 0, smsEnabled: true, maxCreatedAlarms: 0, + maxDebugModeDurationMinutes: 0, tenantServerRestLimitsConfiguration: '', customerServerRestLimitsConfiguration: '', maxWsSessionsPerTenant: 0, From 9c659a7029925e7b46e11874279f0957c14e29ed Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 11:51:16 +0200 Subject: [PATCH 065/103] UI: Refactoring --- .../ota-package-autocomplete.component.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 89b728916f..79fbd36d73 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -31,11 +31,10 @@ import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; import { emptyPageData } from '@shared/models/page/page-data'; -import { getEntityDetailsPageURL } from '@core/utils'; +import { getEntityDetailsPageURL, isDefinedAndNotNull } from '@core/utils'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; @Component({ selector: 'tb-ota-package-autocomplete', @@ -65,19 +64,19 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On this.reset(); } - private deviceProfile: string; + private deviceProfileIdValue: string; get deviceProfileId(): string { - return this.deviceProfile; + return this.deviceProfileIdValue; } @Input() set deviceProfileId(value: string) { - if (this.deviceProfile !== value) { - if (this.deviceProfile) { + if (this.deviceProfileIdValue !== value) { + if (this.deviceProfileIdValue) { this.reset(); } - this.deviceProfile = value ?? NULL_UUID; + this.deviceProfileIdValue = value; } } @@ -258,16 +257,20 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On } fetchPackages(searchText?: string): Observable> { - this.searchText = searchText; - const pageLink = new PageLink(50, 0, searchText, { - property: 'title', - direction: Direction.ASC - }); - return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, - {ignoreLoading: true}).pipe( - catchError(() => of(emptyPageData())), - map((data) => data && data.data.length ? data.data : null) - ); + if (isDefinedAndNotNull(this.deviceProfileId)) { + this.searchText = searchText; + const pageLink = new PageLink(50, 0, searchText, { + property: 'title', + direction: Direction.ASC + }); + return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, + {ignoreLoading: true}).pipe( + catchError(() => of(emptyPageData())), + map((data) => data && data.data.length ? data.data : null) + ); + } else { + return of([]); + } } clear() { From 935739ac85eda9c31f3f9243be494b7dfaeb53f7 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:29:17 +0200 Subject: [PATCH 066/103] UI: Debug Settings minor refactoring --- .../debug}/debug-settings-button.component.html | 0 .../debug}/debug-settings-button.component.ts | 10 +++++----- .../debug}/debug-settings-panel.component.html | 0 .../debug}/debug-settings-panel.component.ts | 4 ++-- .../modules/home/pages/rulechain/rulechain.module.ts | 2 +- ui-ngx/src/app/shared/models/entity.models.ts | 6 +++--- ui-ngx/src/app/shared/models/rule-node.models.ts | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-button.component.html (100%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-button.component.ts (93%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-panel.component.html (100%) rename ui-ngx/src/app/modules/home/components/{debug-settings => entity/debug}/debug-settings-panel.component.ts (97%) diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts index dafda75c0b..f89e7ec198 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts @@ -24,7 +24,7 @@ import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; import { SECOND } from '@shared/models/time/time.models'; -import { DebugSettings } from '@shared/models/entity.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; @@ -79,7 +79,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; - private propagateChange: (settings: DebugSettings) => void = () => {}; + private propagateChange: (settings: EntityDebugSettings) => void = () => {}; constructor(private popoverService: TbPopoverService, private renderer: Renderer2, @@ -126,20 +126,20 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { {}, {}, {}, true); debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; - debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { + debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: EntityDebugSettings) => { this.debugSettingsFormGroup.patchValue(settings); debugStrategyPopover.hide(); }); } } - registerOnChange(fn: (settings: DebugSettings) => void): void { + registerOnChange(fn: (settings: EntityDebugSettings) => void): void { this.propagateChange = fn; } registerOnTouched(_: () => void): void {} - writeValue(settings: DebugSettings): void { + writeValue(settings: EntityDebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts similarity index 97% rename from ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts index a9e237245c..702d416982 100644 --- a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts @@ -32,7 +32,7 @@ import { SECOND } from '@shared/models/time/time.models'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { of, shareReplay, timer } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { DebugSettings } from '@shared/models/entity.models'; +import { EntityDebugSettings } from '@shared/models/entity.models'; import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; @Component({ @@ -78,7 +78,7 @@ export class DebugSettingsPanelComponent extends PageComponent implements OnInit shareReplay(1), ); - onSettingsApplied = new EventEmitter(); + onSettingsApplied = new EventEmitter(); constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index 1fdf11c780..a89b6868f7 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { DebugSettingsButtonComponent } from '@home/components/debug-settings/debug-settings-button.component'; +import { DebugSettingsButtonComponent } from '@home/components/entity/debug-settings/debug-settings-button.component'; @NgModule({ declarations: [ diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index 19b0718336..9934da65aa 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -193,11 +193,11 @@ export interface HasVersion { version?: number; } -export interface HasDebugSettings { - debugSettings?: DebugSettings; +export interface HasEntityDebugSettings { + debugSettings?: EntityDebugSettings; } -export interface DebugSettings { +export interface EntityDebugSettings { failuresEnabled?: boolean; allEnabled?: boolean; allEnabledUntil?: number; diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index ddfad98f11..f7d5e20d1d 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -27,13 +27,13 @@ import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { DebugRuleNodeEventBody } from '@shared/models/event.models'; -import { HasDebugSettings } from '@shared/models/entity.models'; +import { HasEntityDebugSettings } from '@shared/models/entity.models'; export interface RuleNodeConfiguration { [key: string]: any; } -export interface RuleNode extends BaseData, HasDebugSettings { +export interface RuleNode extends BaseData, HasEntityDebugSettings { ruleChainId?: RuleChainId; type: string; name: string; @@ -331,7 +331,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor { configurationDescriptor?: RuleNodeConfigurationDescriptor; } -export interface FcRuleNodeType extends FcNode, HasDebugSettings { +export interface FcRuleNodeType extends FcNode, HasEntityDebugSettings { component?: RuleNodeComponentDescriptor; singletonMode?: boolean; queueName?: string; From 0b57d0ccac6986c523dbb30c85598a10072ea0f1 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:37:20 +0200 Subject: [PATCH 067/103] folder rename --- ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index a89b6868f7..830d539182 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; -import { DebugSettingsButtonComponent } from '@home/components/entity/debug-settings/debug-settings-button.component'; +import { DebugSettingsButtonComponent } from '@home/components/entity/debug/debug-settings-button.component'; @NgModule({ declarations: [ From 74543f80d276fd183d5a11e5766dd29ca37b37b8 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 15:56:53 +0200 Subject: [PATCH 068/103] UI: Debug Settings minor refactoring, files rename --- ...l => entity-debug-settings-button.component.html} | 0 ....ts => entity-debug-settings-button.component.ts} | 12 ++++++------ ...ml => entity-debug-settings-panel.component.html} | 0 ...t.ts => entity-debug-settings-panel.component.ts} | 8 ++++---- .../pages/rulechain/rule-node-details.component.html | 2 +- .../modules/home/pages/rulechain/rulechain.module.ts | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-button.component.html => entity-debug-settings-button.component.html} (100%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-button.component.ts => entity-debug-settings-button.component.ts} (92%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-panel.component.html => entity-debug-settings-panel.component.html} (100%) rename ui-ngx/src/app/modules/home/components/entity/debug/{debug-settings-panel.component.ts => entity-debug-settings-panel.component.ts} (93%) diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts similarity index 92% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts index 3af2ca270c..932658f9bf 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts @@ -29,7 +29,7 @@ import { SharedModule } from '@shared/shared.module'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; -import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; +import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component'; import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; import { of, shareReplay, timer } from 'rxjs'; import { SECOND, MINUTE } from '@shared/models/time/time.models'; @@ -41,8 +41,8 @@ import { Store } from '@ngrx/store'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ - selector: 'tb-debug-settings-button', - templateUrl: './debug-settings-button.component.html', + selector: 'tb-entity-debug-settings-button', + templateUrl: './entity-debug-settings-button.component.html', standalone: true, imports: [ CommonModule, @@ -52,13 +52,13 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DebugSettingsButtonComponent), + useExisting: forwardRef(() => EntityDebugSettingsButtonComponent), multi: true }, ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsButtonComponent implements ControlValueAccessor { +export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { @Input() debugLimitsConfiguration: string; @@ -127,7 +127,7 @@ export class DebugSettingsButtonComponent implements ControlValueAccessor { this.popoverService.hidePopover(trigger); } else { const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, + this.viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null, { ...debugSettings, maxDebugModeDuration: this.maxDebugModeDuration, diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.html rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts similarity index 93% rename from ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts rename to ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts index 2c26e1d3cf..cfa8775a35 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/debug-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.ts @@ -36,8 +36,8 @@ import { EntityDebugSettings } from '@shared/models/entity.models'; import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; @Component({ - selector: 'tb-debug-settings-panel', - templateUrl: './debug-settings-panel.component.html', + selector: 'tb-entity-debug-settings-panel', + templateUrl: './entity-debug-settings-panel.component.html', standalone: true, imports: [ SharedModule, @@ -46,9 +46,9 @@ import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DebugSettingsPanelComponent extends PageComponent implements OnInit { +export class EntityDebugSettingsPanelComponent extends PageComponent implements OnInit { - @Input() popover: TbPopoverComponent; + @Input() popover: TbPopoverComponent; @Input({ transform: booleanAttribute }) failuresEnabled = false; @Input({ transform: booleanAttribute }) allEnabled = false; @Input() allEnabledUntil = 0; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html index 13cc24a7b7..b2d17857b6 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html @@ -35,7 +35,7 @@
- Date: Tue, 10 Dec 2024 16:29:54 +0200 Subject: [PATCH 069/103] UI: Refactoring --- .../widget/lib/home-page/usage-info-widget.component.scss | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index 529a3d4495..86f7036276 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -16,6 +16,8 @@ @import "../../../../../../../scss/constants"; :host { + --mdc-linear-progress-track-height: 8px; + --mdc-linear-progress-active-indicator-height: 8px; .tb-card-header { display: flex; flex-direction: row; @@ -49,7 +51,6 @@ display: none; } .mdc-linear-progress { - height: 8px; margin-top: 6px; margin-bottom: 6px; border-radius: 2px; @@ -97,10 +98,6 @@ :host ::ng-deep { .tb-usage-items-progress { .mat-mdc-progress-bar { - overflow: hidden; - .mdc-linear-progress__bar-inner { - border-top-width: 8px; - } &.critical { .mdc-linear-progress__buffer-bar { background: rgba(209, 39, 48, 0.06); From 42646d4e5c3b7759acae114ddfd10d72482040ab Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 10 Dec 2024 17:14:01 +0200 Subject: [PATCH 070/103] UI: Refactoring css --- .../usage-info-widget.component.scss | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss index 86f7036276..1de8288241 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.scss @@ -16,8 +16,6 @@ @import "../../../../../../../scss/constants"; :host { - --mdc-linear-progress-track-height: 8px; - --mdc-linear-progress-active-indicator-height: 8px; .tb-card-header { display: flex; flex-direction: row; @@ -46,10 +44,18 @@ } .tb-usage-items-progress { + --mdc-linear-progress-track-height: 8px; + --mdc-linear-progress-active-indicator-height: 8px; width: 34px; @media #{$mat-md} { display: none; } + .mat-mdc-progress-bar { + &.critical { + --mdc-linear-progress-track-color: rgba(209, 39, 48, 0.06); + --mdc-linear-progress-active-indicator-color: #D12730; + } + } .mdc-linear-progress { margin-top: 6px; margin-bottom: 6px; @@ -94,18 +100,3 @@ color: rgba(0, 0, 0, 0.76); } } - -:host ::ng-deep { - .tb-usage-items-progress { - .mat-mdc-progress-bar { - &.critical { - .mdc-linear-progress__buffer-bar { - background: rgba(209, 39, 48, 0.06); - } - .mdc-linear-progress__bar-inner { - border-top-color: #D12730; - } - } - } - } -} From 8d6920671dd2ecf3ff86eeb1552701da71b0c028 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 10 Dec 2024 19:23:23 +0200 Subject: [PATCH 071/103] UI: Gateway MQTT md files update --- .../lib/gateway/mqtt-bytes-expression_fn.md | 18 +++++ ...xpressions_fn.md => mqtt-expression_fn.md} | 0 .../lib/gateway/mqtt-json-expression_fn.md | 78 +++++++++++++++++++ ...s_fn.md => mqtt-json-key-expression_fn.md} | 6 +- 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md rename ui-ngx/src/assets/help/en_US/widget/lib/gateway/{expressions_fn.md => mqtt-expression_fn.md} (100%) create mode 100644 ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md rename ui-ngx/src/assets/help/en_US/widget/lib/gateway/{attributes_timeseries_expressions_fn.md => mqtt-json-key-expression_fn.md} (85%) diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md new file mode 100644 index 0000000000..aa7225898a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-bytes-expression_fn.md @@ -0,0 +1,18 @@ +### Expressions +#### Bytes converter: + +For bytes converter, expression fields can use slices format only. A slice specifies how to slice a sequence, determining the start point, and the endpoint. Here's a basic overview of slice components: + +- `start`: The starting index of the slice. It is included in the slice. If omitted, slicing starts at the beginning of the sequence. Indexing starts at 0, so the first element of the sequence is at index 0. + +- `stop`: The ending index of the slice. It is excluded from the slice, meaning the slice will end just before this index. If omitted, slicing goes through the end of the sequence. + +##### Bytes parsing examples: + + +| Message body | Slice | Output data | Description | +|:-----------------------|-----------------|--------------------------|------------------------------| +| AM123,mytype,12.2,45 | [:5] | AM123 | Extracting device name | +| AM123,mytype,12.2,45 | [:] | AM123,mytype,12.2,45 | Extracting all data | +| AM123,mytype,12.2,45 | [18:] | 45 | Extracting humidity value | +| AM123,mytype,12.2,45 | [13:17] | 12.2 | Extracting temperature value | diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md similarity index 100% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-expression_fn.md diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md new file mode 100644 index 0000000000..28387b595b --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-expression_fn.md @@ -0,0 +1,78 @@ +### Expressions +#### JSON Path: + +The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: + +- The JSONPath format can be used to extract data from the message body. + +- The regular expression format can be used to extract data from the topic where the message will arrive. + +- Slices can only be used in the expression fields of bytes converters. + +JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: + +- `$`: The root element of the JSON document; + +- `.`: Child operator used to select child elements. For example, $.store.book ; + +- `[]`: Child operator used to select child elements. $['store']['book'] accesses the book array within a store object; + +##### Examples: + +For example, if we want to extract the device name from the following message, we can use the expression below: + +MQTT message: + +``` +{ + "sensorModelInfo": { + "sensorName": "AM-123", + "sensorType": "myDeviceType" + }, + "data": { + "temp": 12.2, + "hum": 56, + "status": "ok" + } +} +{:copy-code} +``` + +Expression: + +`${sensorModelInfo.sensorName}` + +Converted data: + +`AM-123` + +If we want to extract all data from the message above, we can use the following expression: + +`${data}` + +Converted data: + +`{"temp": 12.2, "hum": 56, "status": "ok"}` + +Or if we want to extract specific data (for example “temperature”), you can use the following expression: + +`${data.temp}` + +And as a converted data we will get: + +`12.2` + +
+ +#### Regular expressions for topic: + +Device name or device profile can be parsed from the MQTT topic using regular expression. A regular expression, often abbreviated as regex or regexp, is a sequence of characters that forms a search pattern, primarily used for string matching and manipulation. + +##### Regular expression for topic examples: + +| Topic | Regular expression | Output data | Description | +|:---------------------------|----------------------------------|--------------------------|--------------------------------------| +| /devices/AM123/mytype/data | /devices/([^/]+)/mytype/data | AM123 | Getting device name from topic | +| /devices/AM123/mytype/data | /devices/[A-Z0-9]+/([^/]+)/data | mytype | Getting device profile from topic | + +
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md similarity index 85% rename from ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md rename to ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md index 91cf23ce26..98337dcd77 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/gateway/attributes_timeseries_expressions_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/gateway/mqtt-json-key-expression_fn.md @@ -3,11 +3,11 @@ The expression field is used to extract data from the MQTT message. There are various available options for different parts of the messages: - - The JSONPath format can be used to extract data from the message body. +- The JSONPath format can be used to extract data from the message body. - - The regular expression format can be used to extract data from the topic where the message will arrive. +- The regular expression format can be used to extract data from the topic where the message will arrive. - - Slices can only be used in the expression fields of bytes converters. +- Slices can only be used in the expression fields of bytes converters. JSONPath expressions specify the items within a JSON structure (which could be an object, array, or nested combination of both) that you want to access. These expressions can select elements from JSON data on specific criteria. Here's a basic overview of how JSONPath expressions are structured: From 9cbe2ca4d5ba55cc98a05645cb075f38880e9769 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 11 Dec 2024 11:14:34 +0200 Subject: [PATCH 072/103] Ignore not chosen recipients for system notification types --- .../server/service/notification/DefaultNotificationCenter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 af63256f0f..d30df7cd65 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 @@ -149,7 +149,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple return; // if originated by rule - just ignore delivery method } } - if (ruleId == null) { + if (ruleId == null && !notificationTemplate.getNotificationType().isSystem()) { if (targets.stream().noneMatch(target -> target.getConfiguration().getType().getSupportedDeliveryMethods().contains(deliveryMethod))) { throw new IllegalArgumentException("Recipients for " + deliveryMethod.getName() + " delivery method not chosen"); } From 072a08564cda53796195c1ba95fe14c4b71e9e20 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 11 Dec 2024 11:28:20 +0200 Subject: [PATCH 073/103] Use tenant notification providers for system notification type --- .../service/notification/DefaultNotificationCenter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d30df7cd65..9a293ada7e 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 @@ -225,13 +225,13 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple NotificationTemplate notificationTemplate = notificationTemplateService.findTenantOrSystemNotificationTemplate(tenantId, type) .orElseThrow(() -> new IllegalArgumentException("No notification template found for type " + type)); NotificationRequest notificationRequest = NotificationRequest.builder() - .tenantId(TenantId.SYS_TENANT_ID) + .tenantId(tenantId) .targets(List.of(targetId.getId())) .templateId(notificationTemplate.getId()) .info(info) .originatorEntityId(TenantId.SYS_TENANT_ID) .build(); - processNotificationRequest(TenantId.SYS_TENANT_ID, notificationRequest, null); + processNotificationRequest(tenantId, notificationRequest, null); } private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { From c218c51db4812e2ff4745458e00fea92bf37c2c4 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 14:59:23 +0200 Subject: [PATCH 074/103] UI: Fixed adaptive grid when moving widget --- .../modules/home/components/dashboard/dashboard.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 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 7169e4a20b..e3e7cb2f1d 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 @@ -253,7 +253,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo draggable: { enabled: this.isEdit && !this.isEditingWidget, delayStart: 100, - stop: (_, itemComponent) => {(itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y);} + stop: (_, itemComponent) => { + (itemComponent.item as DashboardWidget).updatePosition(itemComponent.$item.x, itemComponent.$item.y); + this.notifyGridsterOptionsChanged(); + } }, itemChangeCallback: () => this.dashboardWidgets.sortWidgets(), itemInitCallback: (_, itemComponent) => { From b2a1793f4d2fd3de41b6ac80febec0c24e370f2d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 15:31:16 +0200 Subject: [PATCH 075/103] UI: Fixed updated mobile layout and show mobile edit panel --- .../mobile/bundes/layout/mobile-page-item-row.component.ts | 4 ++-- .../home/pages/mobile/common/editor-panel.component.scss | 1 + .../home/pages/mobile/common/editor-panel.component.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index 9a60c87959..c862a48042 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -270,8 +270,8 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, private updateModel() { this.modelValue.visible = this.mobilePageRowForm.get('visible').value; - const label = this.mobilePageRowForm.get('label').value.trim(); - if (label) { + const label = this.mobilePageRowForm.get('label').value; + if (label?.trim()) { this.modelValue.label = label; } else { delete this.modelValue.label; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss index 00a2ebca2d..87dd67e8e0 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.scss @@ -32,6 +32,7 @@ } .tb-editor { height: 400px; + overflow-y: auto; } .tb-editor-buttons { height: 40px; diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts index 15dff508c8..92ab23f3c2 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts @@ -59,7 +59,7 @@ export class EditorPanelComponent implements OnInit { resize: false, setup: (editor) => { editor.on('PostRender', function() { - const container = editor.getContainer(); + const container = editor.getContainer().closest('.tb-popover-content'); const uiContainer = document.querySelector('.tox.tox-tinymce-aux'); container.parentNode.appendChild(uiContainer); }); From 272a1aa1b165e095d3bbcec7daef5f3c68e0f0d5 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 11 Dec 2024 18:23:15 +0200 Subject: [PATCH 076/103] coaps: x509 - dtls add: DTLS_MAX_FRAGMENT_LENGTH, DTLS_MAX_TRANSMISSION_UNIT --- .../src/main/resources/thingsboard.yml | 4 +++ .../server/coapserver/TbCoapDtlsSettings.java | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index f642d7fea5..5b53339346 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1302,6 +1302,10 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066) + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index f83a20b139..9d3f191f48 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -21,6 +21,7 @@ import org.eclipse.californium.elements.config.Configuration; import org.eclipse.californium.elements.util.SslContextUtil; import org.eclipse.californium.scandium.config.DtlsConnectorConfig; import org.eclipse.californium.scandium.dtls.CertificateType; +import org.eclipse.californium.scandium.dtls.MaxFragmentLengthExtension.Length; import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -44,6 +45,8 @@ import static org.eclipse.californium.elements.config.CertificateAuthenticationM import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_NODE_ID; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_FRAGMENT_LENGTH; +import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_MAX_TRANSMISSION_UNIT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_ROLE; import static org.eclipse.californium.scandium.config.DtlsConfig.DtlsRole.SERVER_ONLY; @@ -66,6 +69,12 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.connection_id_length:}") private Integer cIdLength; + @Value("${coap.dtls.max_transmission_unit:}") + private Integer maxTransmissionUnit; + + @Value("${coap.dtls.max_fragment_length:}") + private Integer maxFragmentLength; + @Bean @ConfigurationProperties(prefix = "coap.dtls.credentials") public SslCredentialsConfig coapDtlsCredentials() { @@ -108,6 +117,15 @@ public class TbCoapDtlsSettings { configBuilder.set(DTLS_CONNECTION_ID_NODE_ID, null); } } + if (maxTransmissionUnit != null) { + configBuilder.set(DTLS_MAX_TRANSMISSION_UNIT, maxTransmissionUnit); + } + if (maxFragmentLength != null) { + Length length = fromLength(maxFragmentLength); + if (length != null) { + configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, fromLength(maxFragmentLength)); + } + } configBuilder.setAdvancedCertificateVerifier( new TbCoapDtlsCertificateVerifier( transportService, @@ -127,4 +145,14 @@ public class TbCoapDtlsSettings { return new InetSocketAddress(addr, port); } + + private static Length fromLength(int length) { + for (Length l : Length.values()) { + if (l.length() == length) { + return l; + } + } + return null; + } } + From 66c9bca5a0901d37cb247b53c952a5c466462936 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 11 Dec 2024 13:21:07 +0200 Subject: [PATCH 077/103] UI: Fixed incorrect copy widget in dashboard --- .../core/services/dashboard-utils.service.ts | 59 +++++++++++++++++++ .../dashboard/dashboard.component.ts | 8 ++- .../home/models/dashboard-component.models.ts | 3 + 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 235924f98f..7e5009f199 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -60,6 +60,7 @@ import { BackgroundType, colorBackground, isBackgroundSettings } from '@shared/m import { MediaBreakpoints } from '@shared/models/constants'; import { TranslateService } from '@ngx-translate/core'; import { DashboardPageLayout } from '@home/components/dashboard-page/dashboard-page.models'; +import { maxGridsterCol, maxGridsterRow } from '@home/models/dashboard-component.models'; @Injectable({ providedIn: 'root' @@ -682,6 +683,10 @@ export class DashboardUtilsService { if (row > -1 && column > - 1) { widgetLayout.row = row; widgetLayout.col = column; + if (this.hasWidgetCollision(widgetLayout.row, widgetLayout.col, + widgetLayout.sizeX, widgetLayout.sizeY, Object.values(layout.widgets))) { + this.widgetPossiblePosition(widgetLayout, layout); + } } else { row = 0; for (const w of Object.keys(layout.widgets)) { @@ -703,6 +708,60 @@ export class DashboardUtilsService { layout.widgets[widget.id] = widgetLayout; } + private widgetPossiblePosition(widgetLayout: WidgetLayout, layout: DashboardLayout) { + let bestRow = 0; + let bestCol = 0; + + let maxCol = layout.gridSettings.minColumns || layout.gridSettings.columns || 0; + let maxRow = 0; + + const widgetLayouts = Object.values(layout.widgets); + + widgetLayouts.forEach(widget => { + maxCol = Math.max(maxCol, widget.col + widget.sizeX); + maxRow = Math.max(maxRow, widget.row + widget.sizeY); + }) + + for (; bestRow < maxRow; bestRow++) { + for (bestCol = 0; bestCol < maxCol; bestCol++) { + if (!this.hasWidgetCollision(bestRow, bestCol, widgetLayout.sizeX, widgetLayout.sizeY, widgetLayouts)) { + widgetLayout.row = bestRow; + widgetLayout.col = bestCol; + return; + } + } + } + const canAddToRows = maxGridsterRow >= maxRow + bestRow; + const canAddToColumns = maxGridsterCol >= maxCol + bestCol; + const addToRows = bestRow <= bestCol && canAddToRows; + if (!addToRows && canAddToColumns) { + widgetLayout.col = maxCol; + widgetLayout.row = 0; + } else if (canAddToRows) { + widgetLayout.row = maxRow; + widgetLayout.col = 0; + } + } + + private hasWidgetCollision(row: number, col: number, sizeX: number, sizeY: number, widgetLayouts: WidgetLayout[]) { + const left = col; + const right = col + sizeX; + const top = row; + const bottom = row + sizeY; + + for (const widget of widgetLayouts) { + const left2 = widget.col; + const right2 = widget.col + widget.sizeX; + const top2 = widget.row; + const bottom2 = widget.row + widget.sizeY; + + if (left < right2 && right > left2 && top < bottom2 && bottom > top2) { + return true; + } + } + return false; + } + public removeWidgetFromLayout(dashboard: Dashboard, targetState: string, targetLayout: DashboardLayoutId, 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 7169e4a20b..d473a6bb67 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 @@ -41,7 +41,9 @@ import { DashboardCallbacks, DashboardWidget, DashboardWidgets, - IDashboardComponent + IDashboardComponent, + maxGridsterCol, + maxGridsterRow } from '../../models/dashboard-component.models'; import { ReplaySubject, Subject, Subscription } from 'rxjs'; import { WidgetLayout, WidgetLayouts } from '@shared/models/dashboard.models'; @@ -231,10 +233,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo disableAutoPositionOnConflict: false, pushItems: false, swap: false, - maxRows: 3000, + maxRows: maxGridsterRow, minCols: this.columns ? this.columns : 24, setGridSize: this.setGridSize, - maxCols: 3000, + maxCols: maxGridsterCol, maxItemCols: 1000, maxItemRows: 1000, maxItemArea: 1000000, diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index 12c00597d5..0de6161e27 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -111,6 +111,9 @@ interface DashboardWidgetUpdateRecord { operation: DashboardWidgetUpdateOperation; } +export const maxGridsterCol = 3000; +export const maxGridsterRow = 3000; + export class DashboardWidgets implements Iterable { highlightedMode = false; From b1179dc4d498b5a0d30eab583cd63c08d6e50b09 Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Wed, 11 Dec 2024 19:16:25 +0200 Subject: [PATCH 078/103] Timewindow: fix applying default grouping interval --- .../time/timewindow-config-dialog.component.html | 10 +++++----- .../time/timewindow-config-dialog.component.ts | 16 ++++++++-------- .../time/timewindow-panel.component.ts | 16 ++++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html index 3574aff0b1..ad71e30f28 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.html @@ -68,7 +68,7 @@ timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL"> @@ -95,7 +95,7 @@ timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL"> @@ -137,7 +137,7 @@ timewindowForm.get('history.historyType').value === historyTypes.LAST_INTERVAL"> @@ -177,7 +177,7 @@ timewindowForm.get('history.historyType').value === historyTypes.INTERVAL"> @@ -199,7 +199,7 @@ formControlName="type" [allowedAggregationTypes]="timewindowForm.get('allowedAggTypes').value"> diff --git a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts index a5a3533b79..a3ab4614e6 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-config-dialog.component.ts @@ -245,9 +245,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -257,9 +257,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('realtime.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -269,9 +269,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.lastAggIntervalsConfig').value; if (lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -281,9 +281,9 @@ export class TimewindowConfigDialogComponent extends PageComponent implements On this.timewindowForm.get('history.advancedParams.quickAggIntervalsConfig').value; if (quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 14247997ac..d3be3e8449 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -308,9 +308,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.realtimeAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('realtime.quickInterval').valueChanges.pipe( @@ -318,9 +318,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.realtimeAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('realtime.interval').patchValue( + setTimeout(() => this.timewindowForm.get('realtime.interval').patchValue( this.realtimeAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.timewindowMs').valueChanges.pipe( @@ -328,9 +328,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((timewindowMs: number) => { if (this.historyAdvancedParams?.lastAggIntervalsConfig?.hasOwnProperty(timewindowMs) && this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.lastAggIntervalsConfig[timewindowMs].defaultAggInterval, {emitEvent: false} - ); + )); } }); this.timewindowForm.get('history.quickInterval').valueChanges.pipe( @@ -338,9 +338,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit, O ).subscribe((quickInterval: number) => { if (this.historyAdvancedParams?.quickAggIntervalsConfig?.hasOwnProperty(quickInterval) && this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval) { - this.timewindowForm.get('history.interval').patchValue( + setTimeout(() => this.timewindowForm.get('history.interval').patchValue( this.historyAdvancedParams.quickAggIntervalsConfig[quickInterval].defaultAggInterval, {emitEvent: false} - ); + )); } }); From 19e0c11b6912dfac2f0b552179ae393eaf51fbf7 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 11:52:48 +0200 Subject: [PATCH 079/103] coaps: x509 - dtls add: DTLS_MAX_FRAGMENT_LENGTH, DTLS_MAX_TRANSMISSION_UNIT add to microservice --- transport/coap/src/main/resources/tb-coap-transport.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 2c1c0550e1..865b85c985 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -198,6 +198,10 @@ coap: # - A value between 0 and <= 4: SingleNodeConnectionIdGenerator is used # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" + # Specify the MTU (Maximum Transmission Unit). + max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" + # DTLS maximum fragment length (RFC 6066) + max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: # Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore) From dc316ec10dcc035e6fadbcf6977a6415ad79ba3f Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 17:38:08 +0200 Subject: [PATCH 080/103] coaps: x509 - dtls reuse length variable --- .../org/thingsboard/server/coapserver/TbCoapDtlsSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 9d3f191f48..6ebc54323d 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -123,7 +123,7 @@ public class TbCoapDtlsSettings { if (maxFragmentLength != null) { Length length = fromLength(maxFragmentLength); if (length != null) { - configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, fromLength(maxFragmentLength)); + configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, length); } } configBuilder.setAdvancedCertificateVerifier( From 4cfe7441b1ab451833d147dcf1ee283911eeb85e Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 12 Dec 2024 19:30:50 +0200 Subject: [PATCH 081/103] coaps: x509 - dtls add default values here (1024) --- .../org/thingsboard/server/coapserver/TbCoapDtlsSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 6ebc54323d..063494d630 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -69,10 +69,10 @@ public class TbCoapDtlsSettings { @Value("${coap.dtls.connection_id_length:}") private Integer cIdLength; - @Value("${coap.dtls.max_transmission_unit:}") + @Value("${coap.dtls.max_transmission_unit:1024}") private Integer maxTransmissionUnit; - @Value("${coap.dtls.max_fragment_length:}") + @Value("${coap.dtls.max_fragment_length:1024}") private Integer maxFragmentLength; @Bean From 9f6bbba7c3457c2cd688710e7466312a70afc80d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 13 Dec 2024 10:43:54 +0200 Subject: [PATCH 082/103] UI: Change mobile-center help link --- ui-ngx/src/app/shared/models/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 6aafef4b83..9bdc7c3c23 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -193,8 +193,8 @@ export const HelpLinks = { scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/`, scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation`, mobileApplication: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, - mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`, + mobileBundle: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/mobile-center/`, + mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/mobile-center/applications/`, } }; /* eslint-enable max-len */ From 50e69681706ddaf25e63f09b77eea789c8f9e836 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 13 Dec 2024 12:13:11 +0200 Subject: [PATCH 083/103] coaps: x509 - dtls add default values here (1024) and in yml add note --- .../src/main/resources/thingsboard.yml | 22 ++++++++++++++++++- .../server/coapserver/TbCoapDtlsSettings.java | 4 ++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 5b53339346..4d69afedb2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1303,8 +1303,28 @@ coap: # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" - # DTLS maximum fragment length (RFC 6066) + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java index 063494d630..2f0127643a 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/TbCoapDtlsSettings.java @@ -117,10 +117,10 @@ public class TbCoapDtlsSettings { configBuilder.set(DTLS_CONNECTION_ID_NODE_ID, null); } } - if (maxTransmissionUnit != null) { + if (maxTransmissionUnit > 0) { configBuilder.set(DTLS_MAX_TRANSMISSION_UNIT, maxTransmissionUnit); } - if (maxFragmentLength != null) { + if (maxFragmentLength > 0) { Length length = fromLength(maxFragmentLength); if (length != null) { configBuilder.set(DTLS_MAX_FRAGMENT_LENGTH, length); From 75e64f7f95c27d3dd9e3dcca40e012cd91f20a54 Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 13 Dec 2024 12:18:09 +0200 Subject: [PATCH 084/103] coaps: x509 - dtls add default values here (1024) and in yml add note --- .../src/main/resources/tb-coap-transport.yml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 865b85c985..0c7ff4e43e 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -199,8 +199,28 @@ coap: # - A value that are > 4: MultiNodeConnectionIdGenerator is used connection_id_length: "${COAP_DTLS_CONNECTION_ID_LENGTH:}" # Specify the MTU (Maximum Transmission Unit). + # Should be used if LAN MTU is not used, e.g. if IP tunnels are used or if the client uses a smaller value than the LAN MTU. + # Default = 1024 + # Minimum value = 64 + # If set to 0 - LAN MTU is used. max_transmission_unit: "${COAP_DTLS_MAX_TRANSMISSION_UNIT:1024}" - # DTLS maximum fragment length (RFC 6066) + # DTLS maximum fragment length (RFC 6066, Section 4). + # Default = 1024 + # Possible values: 512, 1024, 2048, 4096. + # If set to 0, the default maximum fragment size of 2^14 bytes (16,384 bytes) is used. + # Without this extension, TLS specifies a fixed maximum plaintext fragment length of 2^14 bytes. + # It may be desirable for constrained clients to negotiate a smaller maximum fragment length due to memory limitations or bandwidth limitations. + # In order to negotiate smaller maximum fragment lengths, + # clients MAY include an extension of type "max_fragment_length" in the (extended) client hello. + # The "extension_data" field of this extension SHALL contain: + # enum { + # 2^9(1) == 512, + # 2^10(2) == 1024, + # 2^11(3) == 2048, + # 2^12(4) == 4096, + # (255) + # } MaxFragmentLength; + # TLS already requires clients and servers to support fragmentation of handshake messages. max_fragment_length: "${COAP_DTLS_MAX_FRAGMENT_LENGTH:1024}" # Server DTLS credentials credentials: From b401a487f103d9d8a4336c3e538e470ae62e4ad8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 13 Dec 2024 12:37:42 +0200 Subject: [PATCH 085/103] UI[Mobile center]: Add layout label only space validation and improved translation key --- .../layout/custom-mobile-page.component.html | 20 +++++++++++++++++-- .../layout/custom-mobile-page.component.ts | 2 +- .../mobile-page-item-row.component.html | 8 ++++++++ .../layout/mobile-page-item-row.component.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 5 +++-- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html index f543126660..1c89c7e06b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.html @@ -30,6 +30,22 @@ mobile.page-name + + warning + + + warning +
@@ -59,7 +75,7 @@ - {{ 'mobile.url-pattern' | translate }} + {{ 'mobile.invalid-url-format' | translate }} @@ -69,7 +85,7 @@ - {{ 'mobile.path-pattern' | translate }} + {{ 'mobile.invalid-path-format' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts index 5ba2ee3c6f..3075ae772b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/custom-mobile-page.component.ts @@ -60,7 +60,7 @@ export class CustomMobilePageComponent implements ControlValueAccessor, Validato customMobilePageForm = this.fb.group({ visible: [true], icon: ['star'], - label: ['', Validators.required], + label: ['', [Validators.required, Validators.pattern(/\S/)]], type: [MobilePageType.DASHBOARD], dashboardId: this.fb.control(null, Validators.required), url: [{value:'', disabled: true}, [Validators.required, Validators.pattern(/^(https?:\/\/)?(localhost|([\w\-]+\.)+[\w\-]+)(:\d+)?(\/[\w\-._~:\/?#[\]@!$&'()*+,;=%]*)?$/)]], diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html index eeec353103..6ece48086b 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.html @@ -42,6 +42,14 @@ class="tb-error"> warning + + warning + diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts index c862a48042..7083d98860 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/layout/mobile-page-item-row.component.ts @@ -99,7 +99,7 @@ export class MobilePageItemRowComponent implements ControlValueAccessor, OnInit, mobilePageRowForm = this.fb.group({ visible: [true, []], icon: ['', []], - label: ['', []], + label: ['', [Validators.pattern(/\S/)]], type: [MobilePageType.DEFAULT] }); 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 e7157222de..dd73c4776c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3551,6 +3551,7 @@ "max-element-number": "Max elements number", "page-name": "Page name", "page-name-required": "Page name is required.", + "page-name-cannot-contain-only-spaces": "Page name cannot contain only spaces.", "page-type": "Page type", "pages-types": { "dashboard": "Dashboard", @@ -3558,9 +3559,9 @@ "custom": "Custom" }, "url": "URL", - "url-pattern": "Invalid URL", + "invalid-url-format": "Invalid URL format", "path": "Path", - "path-pattern": "Invalid path", + "invalid-path-format": "Invalid path format", "custom-page": "Custom page", "edit-page": "Edit page", "edit-custom-page": "Edit custom page", From efb0cd2681f7812118b9f1ab7253c77de31857bc Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 13 Dec 2024 17:42:01 +0200 Subject: [PATCH 086/103] fixed mobile redirect if ios is disabled in properties --- .../thingsboard/server/controller/QrCodeSettingsController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java index 3e3ffe9f20..76369e7ccd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java @@ -176,7 +176,7 @@ public class QrCodeSettingsController extends BaseController { return ResponseEntity.status(HttpStatus.FOUND) .header("Location", googlePlayLink) .build(); - } else if (userAgent.contains("iPhone") || userAgent.contains("iPad") && qrCodeSettings.isIosEnabled()) { + } else if ((userAgent.contains("iPhone") || userAgent.contains("iPad")) && qrCodeSettings.isIosEnabled()) { String appStoreLink = qrCodeSettings.getAppStoreLink(); return ResponseEntity.status(HttpStatus.FOUND) .header("Location", appStoreLink) From 5cd0f0ceb95bf2efdb6931d9f0e5de1a790a268f Mon Sep 17 00:00:00 2001 From: Ekaterina Chantsova Date: Fri, 13 Dec 2024 17:44:36 +0200 Subject: [PATCH 087/103] Fixed truncateWithTooltip directive behaviour on different mouse events --- .../truncate-with-tooltip.directive.ts | 75 ++++++------------- 1 file changed, 21 insertions(+), 54 deletions(-) diff --git a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts index d37841be6f..9d8f3599fa 100644 --- a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts +++ b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts @@ -14,25 +14,18 @@ /// limitations under the License. /// -import { - AfterViewInit, - Directive, - ElementRef, - Input, - OnDestroy, - OnInit, - Renderer2, -} from '@angular/core'; -import { fromEvent, Subject } from 'rxjs'; -import { filter, takeUntil, tap } from 'rxjs/operators'; +import { AfterViewInit, Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; import { coerceBoolean } from '@shared/decorators/coercion'; @Directive({ selector: '[tbTruncateWithTooltip]', - providers: [MatTooltip], + hostDirectives: [{ + directive: MatTooltip, + inputs: ['matTooltipClass', 'matTooltipTouchGestures'], + }] }) -export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDestroy { +export class TruncateWithTooltipDirective implements OnInit, AfterViewInit { @Input('tbTruncateWithTooltip') text: string; @@ -44,48 +37,31 @@ export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDe @Input() position: TooltipPosition = 'above'; - private destroy$ = new Subject(); - constructor( - private elementRef: ElementRef, + private elementRef: ElementRef, private renderer: Renderer2, private tooltip: MatTooltip ) {} ngOnInit(): void { - this.observeMouseEvents(); this.applyTruncationStyles(); - } - - ngAfterViewInit(): void { this.tooltip.position = this.position; + this.showTooltipOnOverflow(this); } - ngOnDestroy(): void { - if (this.tooltip._isTooltipVisible()) { - this.hideTooltip(); - } - this.destroy$.next(); - this.destroy$.complete(); + ngAfterViewInit(): void { + this.tooltip.message = this.text || this.elementRef.nativeElement.innerText; } - private observeMouseEvents(): void { - fromEvent(this.elementRef.nativeElement, 'mouseenter') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.isOverflown(this.elementRef.nativeElement)), - tap(() => this.showTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); - fromEvent(this.elementRef.nativeElement, 'mouseleave') - .pipe( - filter(() => this.tooltipEnabled), - filter(() => this.tooltip._isTooltipVisible()), - tap(() => this.hideTooltip()), - takeUntil(this.destroy$), - ) - .subscribe(); + private showTooltipOnOverflow(ctx: TruncateWithTooltipDirective) { + ctx.tooltip.show = (function(old) { + function extendsFunction() { + if (ctx.tooltipEnabled && ctx.isOverflown()) { + old.apply(ctx.tooltip, arguments); + } + } + return extendsFunction; + })(ctx.tooltip.show); } private applyTruncationStyles(): void { @@ -94,16 +70,7 @@ export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDe this.renderer.setStyle(this.elementRef.nativeElement, 'text-overflow', 'ellipsis'); } - private isOverflown(element: HTMLElement): boolean { - return element.clientWidth < element.scrollWidth; - } - - private showTooltip(): void { - this.tooltip.message = this.text || this.elementRef.nativeElement.innerText; - this.tooltip.show(); - } - - private hideTooltip(): void { - this.tooltip.hide(); + private isOverflown(): boolean { + return this.elementRef.nativeElement.clientWidth < this.elementRef.nativeElement.scrollWidth; } } From fc905e4e098afd000b7b5050bd0e577913a063b7 Mon Sep 17 00:00:00 2001 From: Daria Shevchenko <116559345+dashevchenko@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:45:57 +0200 Subject: [PATCH 088/103] Added domain validation (#12248) * added domain validation * deleted empty line * fixed controller test * deleted redundant check --- .../server/dao/domain/DomainServiceImpl.java | 4 +++ .../server/dao/service/DataValidator.java | 15 ++++++++- .../validator/DomainDataValidator.java | 32 +++++++++++++++++++ .../server/dao/service/DomainServiceTest.java | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java index 8a735303a7..d349d2d59b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.oauth2.OAuth2ClientDao; +import org.thingsboard.server.dao.service.validator.DomainDataValidator; import java.util.Comparator; import java.util.List; @@ -53,11 +54,14 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe private OAuth2ClientDao oauth2ClientDao; @Autowired private DomainDao domainDao; + @Autowired + private DomainDataValidator domainDataValidator; @Override public Domain saveDomain(TenantId tenantId, Domain domain) { log.trace("Executing saveDomain [{}]", domain); try { + domainDataValidator.validate(domain, Domain::getTenantId); Domain savedDomain = domainDao.save(tenantId, domain); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entityId(savedDomain.getId()).entity(savedDomain).build()); return savedDomain; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index aff7af8165..1321fa5081 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -43,7 +43,10 @@ public abstract class DataValidator> { Pattern.compile("^[A-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); - + private static final String DOMAIN_REGEX = "^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]{1,1}\\.)*(xn--)?([a-z0-9][a-z0-9\\-]{0,60}|[a-z0-9-]{1,30}\\.[a-z]{2,})$"; + private static final Pattern DOMAIN_PATTERN = Pattern.compile(DOMAIN_REGEX); + private static final String LOCALHOST_REGEX = "^localhost(:\\d{1,5})?$"; + private static final Pattern LOCALHOST_PATTERN = Pattern.compile(LOCALHOST_REGEX); private static final String NAME = "name"; private static final String TOPIC = "topic"; @@ -171,4 +174,14 @@ public abstract class DataValidator> { } } + public static boolean isValidDomain(String domainName) { + if (domainName == null) { + return false; + } + if (LOCALHOST_PATTERN.matcher(domainName).matches()) { + return true; + } + return DOMAIN_PATTERN.matcher(domainName).matches(); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java new file mode 100644 index 0000000000..9a9cf99d0e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java @@ -0,0 +1,32 @@ +/** + * 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.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.IncorrectParameterException; + +@Component +public class DomainDataValidator extends AbstractHasOtaPackageValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, Domain domain) { + if (!isValidDomain(domain.getName())) { + throw new IncorrectParameterException("Domain name " + domain.getName() + " is invalid"); + } + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java index 734e30137a..c50991ea87 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DomainServiceTest.java @@ -90,7 +90,7 @@ public class DomainServiceTest extends AbstractServiceTest { public void testGetTenantDomains() { List domains = new ArrayList<>(); for (int i = 0; i < 5; i++) { - Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true, false); + Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5).toLowerCase(), true, false); Domain savedOauth2Client = domainService.saveDomain(SYSTEM_TENANT_ID, oAuth2Client); domains.add(savedOauth2Client); } From c3652bac5fe5ac6d2dd37dc42c103788f0933527 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 16 Dec 2024 12:24:39 +0200 Subject: [PATCH 089/103] UI[Mobile center]: Updated link getting started in flutter doc --- .../bundes/mobile-app-configuration-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html index 25315fca53..4497ece277 100644 --- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-app-configuration-dialog.component.html @@ -72,7 +72,7 @@
mobile.configuration-step.more-information
rocket_launch{{ 'mobile.configuration-step.getting-started' | translate }} From 124d3bfc2758617dfc424bc518e8c1a554b78f9d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 16 Dec 2024 15:10:43 +0200 Subject: [PATCH 090/103] Debug settings minor fixes and adjustments --- ...entity-debug-settings-button.component.html | 4 ++-- .../entity-debug-settings-button.component.ts | 18 ++++++++++-------- .../entity-debug-settings-panel.component.html | 14 +++++++------- .../entity-debug-settings-panel.component.ts | 1 + .../rulechain/rule-node-details.component.html | 1 + .../assets/locale/locale.constant-en_US.json | 5 +++-- 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html index 81563f4bf8..8bc399974c 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html @@ -24,8 +24,8 @@ (click)="openDebugStrategyPanel($event, matButton)"> bug_report @if (isDebugAllActive$ | async) { - {{ !allEnabled() ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} + {{ (allEnabled$ | async) === false ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} } @else { - {{ (failuresEnabled ? 'debug-config.failures' : 'common.disabled') | translate }} + {{ (failuresEnabled ? 'debug-settings.failures' : 'common.disabled') | translate }} } diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts index 932658f9bf..e46112dd9c 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts @@ -21,7 +21,6 @@ import { forwardRef, Input, Renderer2, - signal, ViewContainerRef } from '@angular/core'; import { CommonModule } from '@angular/common'; @@ -30,8 +29,8 @@ import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { TbPopoverService } from '@shared/components/popover.service'; import { MatButton } from '@angular/material/button'; import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component'; -import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop'; -import { of, shareReplay, timer } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { BehaviorSubject, of, shareReplay, timer } from 'rxjs'; import { SECOND, MINUTE } from '@shared/models/time/time.models'; import { EntityDebugSettings } from '@shared/models/entity.models'; import { map, switchMap, takeWhile } from 'rxjs/operators'; @@ -61,6 +60,7 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { @Input() debugLimitsConfiguration: string; + @Input() entityLabel = 'entity'; debugSettingsFormGroup = this.fb.group({ failuresEnabled: [false], @@ -69,9 +69,10 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor }); disabled = false; - allEnabled = signal(false); + private allEnabledSubject = new BehaviorSubject(false); + allEnabled$ = this.allEnabledSubject.asObservable(); - isDebugAllActive$ = toObservable(this.allEnabled).pipe( + isDebugAllActive$ = this.allEnabled$.pipe( switchMap((value) => { if (value) { return of(true); @@ -105,7 +106,7 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( takeUntilDestroyed() - ).subscribe(value => this.allEnabled.set(value)); + ).subscribe(value => this.allEnabledSubject.next(value)); } get failuresEnabled(): boolean { @@ -131,7 +132,8 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor { ...debugSettings, maxDebugModeDuration: this.maxDebugModeDuration, - debugLimitsConfiguration: this.debugLimitsConfiguration + debugLimitsConfiguration: this.debugLimitsConfiguration, + entityLabel: this.entityLabel }, {}, {}, {}, true); @@ -152,7 +154,7 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor writeValue(settings: EntityDebugSettings): void { this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); - this.allEnabled.set(settings?.allEnabled); + this.allEnabledSubject.next(settings?.allEnabled); this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html index d47ee69091..27717c0f54 100644 --- a/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-panel.component.html @@ -16,26 +16,26 @@ -->
-
debug-config.label
+
debug-settings.label
@if (debugLimitsConfiguration) { - {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} + {{ 'debug-settings.hint.main-limited' | translate: { entity: entityLabel, msg: maxMessagesCount, time: (maxTimeFrameDuration | milliSecondsToTimeString: true : true) } }} } @else { - {{ 'debug-config.hint.main' | translate }} + {{ 'debug-settings.hint.main' | translate }} }
-
- {{ 'debug-config.on-failure' | translate }} +
+ {{ 'debug-settings.on-failure' | translate }}
-
- {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }} +
+ {{ 'debug-settings.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) } }}