From 8087ca16efe595da069a7325112f70b85016b3ca Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 3 Nov 2025 08:36:33 +0200 Subject: [PATCH] added default value for metric --- ...AbstractCalculatedFieldProcessingService.java | 16 ++++++++++------ .../utils/CalculatedFieldArgumentUtils.java | 11 +++++++---- application/src/main/resources/thingsboard.yml | 2 ++ .../cf/configuration/aggregation/AggMetric.java | 1 + .../calculated-field-metrics-table.module.ts | 16 ++++++++++++++++ 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java index 1b13bccda1..bae987d53c 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/AbstractCalculatedFieldProcessingService.java @@ -23,6 +23,7 @@ import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; @@ -53,7 +54,6 @@ import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.aggregation.single.AggIntervalEntry; -import org.thingsboard.server.utils.CalculatedFieldArgumentUtils; import java.util.Collections; import java.util.HashMap; @@ -71,6 +71,7 @@ import static org.thingsboard.server.common.data.cf.configuration.geofencing.Ent import static org.thingsboard.server.common.data.cf.configuration.geofencing.EntityCoordinates.ENTITY_ID_LONGITUDE_ARGUMENT_KEY; import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultAttributeEntry; import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.createDefaultKvEntry; +import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformAggMetricArgument; import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformAggregationArgument; import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformSingleValueArgument; import static org.thingsboard.server.utils.CalculatedFieldArgumentUtils.transformTsRollingArgument; @@ -87,6 +88,9 @@ public abstract class AbstractCalculatedFieldProcessingService { protected ListeningExecutorService calculatedFieldCallbackExecutor; + @Value("${actors.calculated_fields.max_datapoints_limit}") + private int aggArgumentMaxDatapointsLimit; + @PostConstruct public void init() { calculatedFieldCallbackExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool( @@ -302,21 +306,21 @@ public abstract class AbstractCalculatedFieldProcessingService { AggFunction function = metric.getFunction(); long intervalMs = interval.getEndTs() - interval.getStartTs(); BaseReadTsKvQuery query = new BaseReadTsKvQuery(argKey, interval.getStartTs(), interval.getEndTs(), intervalMs, 1, Aggregation.valueOf(function.name())); - ListenableFuture argumentEntryFut = fetchTimeSeriesInternal(tenantId, entityId, query, CalculatedFieldArgumentUtils::transformAggMetricArgument); + ListenableFuture argumentEntryFut = fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggMetricArgument(timeSeries, argKey, metric)); return resolveArgumentValue(argKey, argumentEntryFut); } private ListenableFuture fetchTimeSeries(TenantId tenantId, EntityId entityId, Argument argument, AggInterval interval, long queryEndTs) { long startInterval = interval.getCurrentIntervalStartTs(); long intervalEndTs = interval.getCurrentIntervalEndTs(); - ReadTsKvQuery query = buildTimeSeriesQuery(tenantId, argument, startInterval, queryEndTs); + ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), startInterval, queryEndTs, 0, aggArgumentMaxDatapointsLimit, Aggregation.NONE); return fetchTimeSeriesInternal(tenantId, entityId, query, timeSeries -> transformAggregationArgument(timeSeries, startInterval, intervalEndTs)); } private ListenableFuture fetchTsRolling(TenantId tenantId, EntityId entityId, Argument argument, long queryEndTs) { long argTimeWindow = argument.getTimeWindow() == 0 ? queryEndTs : argument.getTimeWindow(); long startInterval = queryEndTs - argTimeWindow; - ReadTsKvQuery query = buildTimeSeriesQuery(tenantId, argument, startInterval, queryEndTs); + ReadTsKvQuery query = buildTsRollingQuery(tenantId, argument, startInterval, queryEndTs); return fetchTimeSeriesInternal(tenantId, entityId, query, tsRolling -> transformTsRollingArgument(tsRolling, query.getLimit(), argTimeWindow)); } @@ -352,10 +356,10 @@ public abstract class AbstractCalculatedFieldProcessingService { }, calculatedFieldCallbackExecutor); } - private ReadTsKvQuery buildTimeSeriesQuery(TenantId tenantId, Argument argument, long startTs, long endTs) { + private ReadTsKvQuery buildTsRollingQuery(TenantId tenantId, Argument argument, long startTs, long endTs) { long maxDataPoints = apiLimitService.getLimit( tenantId, DefaultTenantProfileConfiguration::getMaxDataPointsPerRollingArg); - int argumentLimit = argument.getLimit() == null ? 500000 : argument.getLimit(); + int argumentLimit = argument.getLimit(); int limit = argumentLimit == 0 || argumentLimit > maxDataPoints ? (int) maxDataPoints : argumentLimit; return new BaseReadTsKvQuery(argument.getRefEntityKey().getKey(), startTs, endTs, 0, limit, Aggregation.NONE); } diff --git a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java index c6c64782a9..676903e239 100644 --- a/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/CalculatedFieldArgumentUtils.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.MoreExecutors; import org.apache.commons.lang3.math.NumberUtils; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.cf.configuration.Argument; +import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; @@ -67,9 +68,9 @@ public class CalculatedFieldArgumentUtils { return ArgumentEntry.createTsRollingArgument(tsRolling, limit, argTimeWindow); } - public static ArgumentEntry transformAggMetricArgument(List timeSeries) { + public static ArgumentEntry transformAggMetricArgument(List timeSeries, String argKey, AggMetric aggMetric) { if (timeSeries == null || timeSeries.isEmpty()) { - return new SingleValueArgumentEntry(); + return ArgumentEntry.createSingleValueArgument(createDefaultKvEntry(argKey, aggMetric.getDefaultValue())); } return ArgumentEntry.createSingleValueArgument(timeSeries.get(0)); } @@ -86,8 +87,10 @@ public class CalculatedFieldArgumentUtils { } public static KvEntry createDefaultKvEntry(Argument argument) { - String key = argument.getRefEntityKey().getKey(); - String defaultValue = argument.getDefaultValue(); + return createDefaultKvEntry(argument.getRefEntityKey().getKey(), argument.getDefaultValue()); + } + + public static KvEntry createDefaultKvEntry(String key, String defaultValue) { if (StringUtils.isBlank(defaultValue)) { return new StringDataEntry(key, null); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 88f2f017d3..b29970417a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -531,6 +531,8 @@ actors: calculation_timeout: "${ACTORS_CALCULATION_TIMEOUT_SEC:5}" # Interval in seconds to re-evaluate calculated fields that have a time schedule. 2 minutes by default. check_interval: "${ACTORS_CALCULATED_FIELDS_CHECK_INTERVAL_SEC:120}" + # Maximum allowed datapoints fetched by aggregation calculated fields + max_datapoints_limit: "${CF_AGG_MAX_DATAPOINTS_LIMIT:50000}" debug: settings: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java index ebd612b1e0..8bf2818bd2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/AggMetric.java @@ -27,5 +27,6 @@ public class AggMetric { private AggFunction function; private String filter; private AggInput input; + private String defaultValue; } diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts index 10d391a0a4..14a6a3963f 100644 --- a/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts +++ b/ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.module.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 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 { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module';