diff --git a/application/src/main/data/json/system/widget_types/html_container.json b/application/src/main/data/json/system/widget_types/html_container.json index 70818154af..25b9e5ac5d 100644 --- a/application/src/main/data/json/system/widget_types/html_container.json +++ b/application/src/main/data/json/system/widget_types/html_container.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n \n}\n", + "controllerScript": "self.onInit = function() {\n \n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '100%',\n previewHeight: '100%',\n overflowVisible: true\n };\n};\n", "settingsDirective": "tb-html-container-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-html-container-basic-config", diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java index 930d030d71..f9c06dbda7 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java @@ -179,6 +179,7 @@ public class AlarmCalculatedFieldState extends BaseCalculatedFieldState { ruleState.setActive(null); AlarmCondition condition = rule.getCondition(); if (condition.hasSchedule() || (condition.getType() == AlarmConditionType.DURATION && !ruleState.isEmpty())) { + ruleState.cancelDurationCheckFuture(); reevalNeeded.set(true); } } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java index 19c48272cc..ada94dacda 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java @@ -256,10 +256,7 @@ public class AlarmRuleState { firstEventTs = 0L; lastCheckTs = 0L; duration = 0L; - if (durationCheckFuture != null) { - durationCheckFuture.cancel(true); - durationCheckFuture = null; - } + cancelDurationCheckFuture(); } public void setDurationCheckFuture(ScheduledFuture durationCheckFuture) { @@ -270,6 +267,13 @@ public class AlarmRuleState { this.durationCheckFuture = durationCheckFuture; } + public void cancelDurationCheckFuture() { + if (durationCheckFuture != null) { + durationCheckFuture.cancel(true); + durationCheckFuture = null; + } + } + public boolean isEmpty() { return eventCount == 0L && firstEventTs == 0L && lastCheckTs == 0L && durationCheckFuture == null; } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java index b03a460c31..2452bd8d61 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbelCfTsRollingArg.java @@ -73,7 +73,7 @@ public class TbelCfTsRollingArg implements TbelCfArg, Iterable service.init()) + .isInstanceOf(BindException.class); + + EventLoopGroup boss = (EventLoopGroup) ReflectionTestUtils.getField(service, "bossGroup"); + EventLoopGroup worker = (EventLoopGroup) ReflectionTestUtils.getField(service, "workerGroup"); + + assertThat(boss).isNotNull(); + assertThat(worker).isNotNull(); + assertThat(boss.isShuttingDown()).isTrue(); + assertThat(worker.isShuttingDown()).isTrue(); + + await().atMost(30, TimeUnit.SECONDS).until(boss::isTerminated); + await().atMost(30, TimeUnit.SECONDS).until(worker::isTerminated); + } + + @Test + public void whenSslBindFailsAfterPlainBound_thenInitThrowsAndClosesPlainChannelAndReleasesNettyResources() { + ReflectionTestUtils.setField(service, "port", 0); + ReflectionTestUtils.setField(service, "sslEnabled", true); + ReflectionTestUtils.setField(service, "sslPort", occupiedPort); + + assertThatThrownBy(() -> service.init()) + .isInstanceOf(BindException.class); + + Channel serverChannel = (Channel) ReflectionTestUtils.getField(service, "serverChannel"); + Channel sslServerChannel = (Channel) ReflectionTestUtils.getField(service, "sslServerChannel"); + EventLoopGroup boss = (EventLoopGroup) ReflectionTestUtils.getField(service, "bossGroup"); + EventLoopGroup worker = (EventLoopGroup) ReflectionTestUtils.getField(service, "workerGroup"); + + assertThat(serverChannel).isNotNull(); + assertThat(sslServerChannel).isNull(); + assertThat(boss).isNotNull(); + assertThat(worker).isNotNull(); + + await().atMost(10, TimeUnit.SECONDS).until(() -> !serverChannel.isOpen()); + + assertThat(boss.isShuttingDown()).isTrue(); + assertThat(worker.isShuttingDown()).isTrue(); + await().atMost(30, TimeUnit.SECONDS).until(boss::isTerminated); + await().atMost(30, TimeUnit.SECONDS).until(worker::isTerminated); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java index 6cfe1c7b36..10c5ae4185 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java @@ -61,8 +61,10 @@ public interface TsKvRepository extends JpaRepository processMinOrMaxResult(AggregationResult aggResult) { if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { if (aggResult.hasDouble) { - double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE); + double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(-Double.MAX_VALUE); double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE); return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL)))); } else { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java index 221684c10c..e7d2d6a8fc 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java @@ -710,6 +710,31 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { assertEquals(java.util.Optional.of(2L), list.get(2).getLongValue()); } + @Test + public void testFindDeviceMaxAggregationOverNegativeMixedLongAndDoubleTsData() throws Exception { + save(deviceId, 5000, -100L); + save(deviceId, 15000, -50.0); + + List list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, + 60000, 60000, 1, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + + assertEquals(1, list.size()); + assertEquals(java.util.Optional.of(-50.0), list.get(0).getDoubleValue()); + } + + @Test + public void testFindDeviceMaxAggregationOverAllNegativeDoubleTsData() throws Exception { + save(deviceId, 5000, -50.0); + save(deviceId, 15000, -100.0); + save(deviceId, 25000, -75.0); + + List list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, + 60000, 60000, 1, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS); + + assertEquals(1, list.size()); + assertEquals(java.util.Optional.of(-50.0), list.get(0).getDoubleValue()); + } + @Test public void testSaveTs_RemoveTs_AndSaveTsAgain() throws Exception { save(deviceId, 2000000L, 95); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.models.ts index 87e5edbe41..b974e15fb2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/html/html-container-widget.models.ts @@ -51,13 +51,18 @@ const containerFunctionCompletions: TbEditorCompletions = { type: widgetContextCompletions.ctx.type, description: widgetContextCompletions.ctx.description, children: widgetContextCompletions.ctx.children - }, + } + } +}; + +export const AngularContainerFunctionEditorCompleter = new TbEditorCompleter(containerFunctionCompletions); + +export const HTMLContainerFunctionEditorCompleter = new TbEditorCompleter( + {...containerFunctionCompletions, container: { meta: 'argument', type: 'HTMLElement', description: 'Container element of the widget' - }, - } -}; + }} +); -export const ContainerFunctionEditorCompleter = new TbEditorCompleter(containerFunctionCompletions); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html index 4ccf475dc9..a72f28e70e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.html @@ -17,82 +17,112 @@ -->
-
-
widgets.html-container.container-type
+
{{ 'widgets.html-container.type-plain' | translate }} {{ 'widgets.html-container.type-angular' | translate }}
- - - -
{{ 'widgets.html-container.resources' | translate }}
-
-
- @if (resourcesFormArray.length) { - @for (resourceControl of resourcesControls; track resourceControl; let i = $index) { -
- - - @if (htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR) { - - {{ 'widget.resource-is-extension' | translate }} - +
+
+ +
+
+ + + +
{{ 'widgets.html-container.resources' | translate }}
+
+
+ @if (resourcesFormArray.length) { + @for (resourceControl of resourcesControls; track resourceControl; let i = $index) { +
+ + + @if (htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR) { + + {{ 'widget.resource-is-extension' | translate }} + + } + +
} -
- } - } @else { - widgets.html-container.no-resources +
+ + + + + + + + + + @if (!fullscreen) { + + + + } + +
+ @if (fullscreen) { + } -
- -
- - - - - - - - - - - - - - +
+
+ + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss index d2385459e0..c2db82139d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.scss @@ -14,15 +14,115 @@ * limitations under the License. */ - :host { - &.tb-html-container-settings { +.tb-html-container-settings { + height: 100%; +} + +.tb-html-container-settings .tb-html-container-settings-panel, .tb-html-container-settings-panel { + position: relative; + background: #fff; + .mat-mdc-tab-body-wrapper { + position: relative; + top: 0; + flex: 1; + } + .tb-action-expand-button { + position: absolute; + top: 4px; + right: 0; + z-index: 2; + } + .gutter { + display: none; + background-color: #eee; + background-repeat: no-repeat; + background-position: 50%; + &.gutter-horizontal { + cursor: col-resize; + background-image: url("../../../../../../../../../assets/split.js/grips/vertical.png"); + } + } + .tb-js-func { + &:not(.tb-fullscreen) { + &.tb-hide-brackets { + padding-bottom: 0; + } + } + } + .tb-html { + position: relative; + &:not(.tb-fullscreen) { + padding-bottom: 0; + } + .tb-html-toolbar { + position: absolute; + top: 0; + right: 8px; + z-index: 8; + .tb-title { + display: none; + } + } + .tb-html-content-panel { + border-top: none; + height: 100%; + } + } + .tb-css { + position: relative; + &:not(.tb-fullscreen) { + .tb-css-content-panel { + margin: 0; + } + } + .tb-css-toolbar { + position: absolute; + top: 0; + right: 8px; + z-index: 8; + .tb-title { + display: none; + } + } + .tb-css-content-panel { + border-top: none; height: 100%; - ::ng-deep { - .mat-mdc-tab-body-wrapper { - position: relative; - top: 0; - flex: 1; + } + } + &.tb-fullscreen { + padding: 8px; + gap: 8px; + .tb-action-expand-button { + position: relative; + top: 0; + right: 0; + } + .gutter { + display: block; + } + .tb-content { + border: 1px solid #c0c0c0; + .tb-html { + .tb-html-content-panel { + border: none; + } + } + .tb-css { + .tb-css-content-panel { + border: none; + } + } + .tb-js-func { + padding-top: 8px; + .tb-js-func-toolbar { + padding: 0 5px; + } + .tb-js-func-panel { + border-left: none; + border-right: none; + border-bottom: none; } } } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts index 116d69d468..121362ef57 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/html/html-container-settings.component.ts @@ -14,7 +14,18 @@ /// limitations under the License. /// -import { Component, DestroyRef, forwardRef, HostBinding, Input, OnInit } from '@angular/core'; +import { + AfterViewInit, + Component, + DestroyRef, + ElementRef, + forwardRef, + HostBinding, + Input, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { WidgetResource } from '@shared/models/widget.models'; import { ControlValueAccessor, @@ -28,7 +39,8 @@ import { Validators } from '@angular/forms'; import { - ContainerFunctionEditorCompleter, + AngularContainerFunctionEditorCompleter, + HTMLContainerFunctionEditorCompleter, HtmlContainerWidgetSettings, HtmlContainerWidgetType } from '@home/components/widget/lib/html/html-container-widget.models'; @@ -52,23 +64,39 @@ import { WidgetService } from '@core/http/widget.service'; multi: true, } ], + encapsulation: ViewEncapsulation.None, standalone: false }) -export class HtmlContainerSettingsComponent implements OnInit, ControlValueAccessor, Validator { +export class HtmlContainerSettingsComponent implements OnInit, AfterViewInit, ControlValueAccessor, Validator { HtmlContainerWidgetType = HtmlContainerWidgetType; functionScopeVariables = this.widgetService.getWidgetScopeVariables(); - containerFunctionEditorCompleter = ContainerFunctionEditorCompleter; + get containerFunctionEditorCompleter() { + return this.htmlContainerSettingsForm.get('type').value === HtmlContainerWidgetType.ANGULAR + ? AngularContainerFunctionEditorCompleter + : HTMLContainerFunctionEditorCompleter; + } @HostBinding('class') hostClass = 'tb-html-container-settings'; + @ViewChild('leftPanel', { read: ElementRef }) + leftPanelElmRef!: ElementRef; + + @ViewChild('rightPanel', { read: ElementRef }) + rightPanelElmRef!: ElementRef; + @Input() disabled: boolean; + fullscreen = false; + + tabsAnimationDuration = '500ms'; + htmlContainerSettingsForm: UntypedFormGroup; + private modelValue: HtmlContainerWidgetSettings; constructor(private fb: UntypedFormBuilder, @@ -102,11 +130,26 @@ export class HtmlContainerSettingsComponent implements OnInit, ControlValueAcces }); } + ngAfterViewInit(): void { + if (this.leftPanelElmRef && this.rightPanelElmRef) { + this.initSplitLayout(this.leftPanelElmRef.nativeElement, + this.rightPanelElmRef.nativeElement); + } + } + + private initSplitLayout(leftPanel: any, rightPanel: any) { + Split([leftPanel, rightPanel], { + sizes: [50, 50], + gutterSize: 8, + cursor: 'col-resize' + }); + } + registerOnChange(fn: any): void { this.propagateChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(_fn: any): void { } setDisabledState(isDisabled: boolean): void { @@ -150,7 +193,15 @@ export class HtmlContainerSettingsComponent implements OnInit, ControlValueAcces this.resourcesFormArray.removeAt(index); } - private propagateChange = (v: any) => { }; + toggleFullScreen(): void { + this.fullscreen = !this.fullscreen; + this.tabsAnimationDuration = '0ms'; + setTimeout(() => { + this.tabsAnimationDuration = '500ms'; + }); + } + + private propagateChange = (_v: any) => { }; private updateModel() { this.modelValue = this.htmlContainerSettingsForm.value; diff --git a/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts b/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts index 38cc0b52f4..94eced7fcf 100644 --- a/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts +++ b/ui-ngx/src/app/shared/models/widget/maps/map-model.definition.ts @@ -27,8 +27,10 @@ import { import { additionalMapDataSourcesToDatasources, BaseMapSettings, + latestMapDataLayerTypes, MapDataLayerSettings, MapDataLayerType, + mapDataLayerTypes, MapDataSourceSettings, mapDataSourceSettingsToDatasource, MapType @@ -46,30 +48,21 @@ interface MapDataLayerDsInfo extends AliasFilterPair { type ExportDataSourceInfo = {[dataLayerIndex: number]: MapDataLayerDsInfo}; -interface MapDatasourcesInfo { - trips?: ExportDataSourceInfo; - markers?: ExportDataSourceInfo; - polygons?: ExportDataSourceInfo; - circles?: ExportDataSourceInfo; +type MapDatasourcesInfo = { + [K in MapDataLayerType]?: ExportDataSourceInfo; +} & { additionalDataSources?: ExportDataSourceInfo; -} +}; export const MapModelDefinition: WidgetModelDefinition = { testWidget(widget: Widget): boolean { if (widget?.config?.settings) { const settings = widget.config.settings; if (settings.mapType && [MapType.image, MapType.geoMap].includes(settings.mapType)) { - if (settings.trips && Array.isArray(settings.trips)) { - return true; - } - if (settings.markers && Array.isArray(settings.markers)) { - return true; - } - if (settings.polygons && Array.isArray(settings.polygons)) { - return true; - } - if (settings.circles && Array.isArray(settings.circles)) { - return true; + for (const layerType of mapDataLayerTypes) { + if (Array.isArray(settings[layerType])) { + return true; + } } } } @@ -78,17 +71,11 @@ export const MapModelDefinition: WidgetModelDefinition = { prepareExportInfo(dashboard: Dashboard, widget: Widget): MapDatasourcesInfo { const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; const info: MapDatasourcesInfo = {}; - if (settings.trips?.length) { - info.trips = prepareExportDataSourcesInfo(dashboard, settings.trips); - } - if (settings.markers?.length) { - info.markers = prepareExportDataSourcesInfo(dashboard, settings.markers); - } - if (settings.polygons?.length) { - info.polygons = prepareExportDataSourcesInfo(dashboard, settings.polygons); - } - if (settings.circles?.length) { - info.circles = prepareExportDataSourcesInfo(dashboard, settings.circles); + for (const layerType of mapDataLayerTypes) { + const dataLayerSettings = settings[layerType]; + if (dataLayerSettings?.length) { + info[layerType] = prepareExportDataSourcesInfo(dashboard, dataLayerSettings); + } } if (settings.additionalDataSources?.length) { info.additionalDataSources = prepareExportDataSourcesInfo(dashboard, settings.additionalDataSources); @@ -96,59 +83,36 @@ export const MapModelDefinition: WidgetModelDefinition = { return info; }, updateFromExportInfo(widget: Widget, entityAliases: EntityAliases, filters: Filters, info: MapDatasourcesInfo): void { - const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; - if (info?.trips) { - updateMapDatasourceFromExportInfo(entityAliases, filters, settings.trips, info.trips); - } - if (info?.markers) { - updateMapDatasourceFromExportInfo(entityAliases, filters, settings.markers, info.markers); - } - if (info?.polygons) { - updateMapDatasourceFromExportInfo(entityAliases, filters, settings.polygons, info.polygons); - } - if (info?.circles) { - updateMapDatasourceFromExportInfo(entityAliases, filters, settings.circles, info.circles); - } - if (info?.additionalDataSources) { - updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources); + if (info && Object.keys(info).length) { + const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; + for (const layerType of mapDataLayerTypes) { + const layerInfo = info[layerType]; + const dataLayerSettings = settings[layerType]; + if (layerInfo && dataLayerSettings) { + updateMapDatasourceFromExportInfo(entityAliases, filters, dataLayerSettings, layerInfo); + } + } + if (info.additionalDataSources) { + updateMapDatasourceFromExportInfo(entityAliases, filters, settings.additionalDataSources, info.additionalDataSources); + } } }, datasources(widget: Widget): Datasource[] { - const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; - const datasources: Datasource[] = []; - if (settings.trips?.length) { - datasources.push(...getMapDataLayersDatasources(settings.trips)); - } - if (settings.markers?.length) { - datasources.push(...getMapDataLayersDatasources(settings.markers)); - } - if (settings.polygons?.length) { - datasources.push(...getMapDataLayersDatasources(settings.polygons)); - } - if (settings.circles?.length) { - datasources.push(...getMapDataLayersDatasources(settings.circles)); - } - if (settings.additionalDataSources?.length) { - datasources.push(...additionalMapDataSourcesToDatasources(settings.additionalDataSources)); - } - return datasources; + return getMapDataLayersDatasources(widget.config.settings as BaseMapSettings, mapDataLayerTypes); }, hasTimewindow(widget: Widget): boolean { const settings: BaseMapSettings = widget.config.settings as BaseMapSettings; - if (settings.trips?.length) { + const timeSeriesDataLayerTypes = mapDataLayerTypes.filter(t => !latestMapDataLayerTypes.includes(t)); + if (timeSeriesDataLayerTypes.some(layerType => settings[layerType]?.length)) { return true; - } else { - const datasources: Datasource[] = getMapLatestDataLayersDatasources(settings); - return datasourcesHasAggregation(datasources); } + return datasourcesHasAggregation(getMapDataLayersDatasources(settings, latestMapDataLayerTypes, true)); }, datasourcesHasAggregation(widget: Widget): boolean { - const datasources: Datasource[] = getMapLatestDataLayersDatasources(widget.config.settings as BaseMapSettings); - return datasourcesHasAggregation(datasources); + return datasourcesHasAggregation(getMapDataLayersDatasources(widget.config.settings as BaseMapSettings, latestMapDataLayerTypes, true)); }, datasourcesHasOnlyComparisonAggregation(widget: Widget): boolean { - const datasources: Datasource[] = getMapLatestDataLayersDatasources(widget.config.settings as BaseMapSettings); - return datasourcesHasOnlyComparisonAggregation(datasources); + return datasourcesHasOnlyComparisonAggregation(getMapDataLayersDatasources(widget.config.settings as BaseMapSettings, latestMapDataLayerTypes, true)); } }; @@ -236,7 +200,7 @@ const prepareAliasAndFilterPair = (dashboard: Dashboard, settings: MapDataSource } } -const getMapDataLayersDatasources = (settings: MapDataLayerSettings[], +const getMapDataLayerDatasources = (settings: MapDataLayerSettings[], includeDataKeys = false, dataLayerType?: MapDataLayerType): Datasource[] => { const datasources: Datasource[] = []; settings.forEach((dsSettings) => { @@ -255,16 +219,14 @@ const getMapDataLayersDatasources = (settings: MapDataLayerSettings[], return datasources; }; -const getMapLatestDataLayersDatasources = (settings: BaseMapSettings): Datasource[] => { +const getMapDataLayersDatasources = (settings: BaseMapSettings, + layerTypes: readonly MapDataLayerType[], includeDataKeys = false): Datasource[] => { const datasources: Datasource[] = []; - if (settings.markers?.length) { - datasources.push(...getMapDataLayersDatasources(settings.markers, true, 'markers')); - } - if (settings.polygons?.length) { - datasources.push(...getMapDataLayersDatasources(settings.polygons, true, 'polygons')); - } - if (settings.circles?.length) { - datasources.push(...getMapDataLayersDatasources(settings.circles, true, 'circles')); + for (const layerType of layerTypes) { + const dataLayerSettings = settings[layerType]; + if (dataLayerSettings?.length) { + datasources.push(...getMapDataLayerDatasources(dataLayerSettings, includeDataKeys, layerType)); + } } if (settings.additionalDataSources?.length) { datasources.push(...additionalMapDataSourcesToDatasources(settings.additionalDataSources)); diff --git a/ui-ngx/src/app/shared/models/widget/maps/map.models.ts b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts index 69f6fd02f1..1d82139050 100644 --- a/ui-ngx/src/app/shared/models/widget/maps/map.models.ts +++ b/ui-ngx/src/app/shared/models/widget/maps/map.models.ts @@ -200,9 +200,11 @@ export const defaultBaseDataLayerSettings = (mapType: MapType): Partial { if (!dataLayer.dsType || ![DatasourceType.function, DatasourceType.device, DatasourceType.entity].includes(dataLayer.dsType)) {