diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java index 9153f31447..8bb78c29dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/BaseCalculatedFieldState.java @@ -21,6 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; @@ -142,8 +143,8 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, protected void validateNewEntry(String key, ArgumentEntry newEntry) { } - protected ObjectNode toSimpleResult(boolean useLatestTs, ObjectNode valuesNode) { - if (!useLatestTs) { + protected ObjectNode toResultNode(ObjectNode valuesNode) { + if (ctx.getOutput().getType() == OutputType.ATTRIBUTES || !ctx.isUseLatestTs()) { return valuesNode; } long latestTs = getLatestTimestamp(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java index 84a0ab008f..ab38287b89 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ExpressionBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.PropagationCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey; @@ -187,7 +188,6 @@ public class CalculatedFieldCtx implements Closeable { .collect(Collectors.toList()); if (argBasedConfig instanceof ExpressionBasedCalculatedFieldConfiguration expressionBasedConfig) { this.expression = expressionBasedConfig.getExpression(); - this.useLatestTs = CalculatedFieldType.SIMPLE.equals(calculatedField.getType()) && ((SimpleCalculatedFieldConfiguration) argBasedConfig).isUseLatestTs(); } if (calculatedField.getConfiguration() instanceof GeofencingCalculatedFieldConfiguration geofencingConfig) { geofencingConfig.getZoneGroups().forEach((zoneGroupName, config) -> { @@ -209,8 +209,8 @@ public class CalculatedFieldCtx implements Closeable { if (calculatedField.getConfiguration() instanceof ScheduledUpdateSupportedCalculatedFieldConfiguration scheduledConfig) { this.scheduledUpdateIntervalMillis = scheduledConfig.isScheduledUpdateEnabled() ? TimeUnit.SECONDS.toMillis(scheduledConfig.getScheduledUpdateInterval()) : DISABLED_INTERVAL_VALUE; } - if (calculatedField.getConfiguration() instanceof RelatedEntitiesAggregationCalculatedFieldConfiguration aggConfig) { - this.useLatestTs = aggConfig.isUseLatestTs(); + if (calculatedField.getConfiguration() instanceof HasUseLatestTsConfig hasUseLatestTsConfig) { + this.useLatestTs = hasUseLatestTsConfig.isUseLatestTs(); } this.systemContext = systemContext; this.tbelInvokeService = systemContext.getTbelInvokeService(); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java index 0ec0a941d7..b9fbe89bb1 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java @@ -53,7 +53,7 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { Output output = ctx.getOutput(); Object result = NumberUtils.roundResult(expressionResult, output.getDecimalsByDefault()); - JsonNode outputResult = createResultJson(ctx.isUseLatestTs(), output.getName(), result); + JsonNode outputResult = createResultJson(output.getName(), result); return Futures.immediateFuture(TelemetryCalculatedFieldResult.builder() .outputStrategy(output.getStrategy()) @@ -63,7 +63,7 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { .build()); } - private JsonNode createResultJson(boolean useLatestTs, String outputName, Object result) { + private JsonNode createResultJson(String outputName, Object result) { ObjectNode valuesNode = JacksonUtil.newObjectNode(); if (result instanceof Double doubleValue) { valuesNode.put(outputName, doubleValue); @@ -72,7 +72,7 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { } else { valuesNode.set(outputName, JacksonUtil.valueToTree(result)); } - return toSimpleResult(useLatestTs, valuesNode); + return toResultNode(valuesNode); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java index 868e8a15ae..5c5858e3bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/aggregation/RelatedEntitiesAggregationCalculatedFieldState.java @@ -201,7 +201,7 @@ public class RelatedEntitiesAggregationCalculatedFieldState extends BaseCalculat .outputStrategy(output.getStrategy()) .type(output.getType()) .scope(output.getScope()) - .result(toSimpleResult(ctx.isUseLatestTs(), aggResult)) + .result(toResultNode(aggResult)) .build()); } else { return Futures.immediateFuture(TelemetryCalculatedFieldResult.EMPTY); diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java index 05cc378209..ea669998bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingCalculatedFieldState.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.cf.ctx.state.geofencing; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -136,7 +135,7 @@ public class GeofencingCalculatedFieldState extends BaseCalculatedFieldState imp .outputStrategy(ctx.getOutput().getStrategy()) .type(outputType) .scope(ctx.getOutput().getScope()) - .result(toResultNode(outputType, valuesNode)) + .result(toResultNode(valuesNode)) .build(); if (relationFutures.isEmpty()) { return Futures.immediateFuture(result); @@ -186,10 +185,6 @@ public class GeofencingCalculatedFieldState extends BaseCalculatedFieldState imp } } - private JsonNode toResultNode(OutputType outputType, ObjectNode valuesNode) { - return toSimpleResult(outputType == OutputType.TIME_SERIES, valuesNode); - } - private GeofencingEvalResult aggregateZoneGroup(List zoneResults) { boolean nowInside = zoneResults.stream().anyMatch(r -> INSIDE.equals(r.status())); boolean prevInside = zoneResults.stream() diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java index d7c43b01d7..7ab0a51af3 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/propagation/PropagationCalculatedFieldState.java @@ -24,7 +24,6 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Output; -import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; @@ -171,8 +170,7 @@ public class PropagationCalculatedFieldState extends ScriptCalculatedFieldState throw new IllegalArgumentException("Unsupported argument type: " + argumentEntry.getType() + " detected for argument: " + outputKey + ". " + "Only Latest telemetry or Attribute arguments supported for 'Arguments Only' propagation mode!"); }); - ObjectNode result = toSimpleResult(output.getType() == OutputType.TIME_SERIES, valuesNode); - telemetryCfBuilder.result(result); + telemetryCfBuilder.result(toResultNode(valuesNode)); return telemetryCfBuilder.build(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java index e17b50ac98..d2c6ebb78c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.cf.configuration; +import jakarta.validation.constraints.NotNull; import lombok.Data; import java.util.Map; @@ -24,6 +25,8 @@ public abstract class BaseCalculatedFieldConfiguration implements ExpressionBase protected Map arguments; protected String expression; + + @NotNull protected Output output; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java new file mode 100644 index 0000000000..f8fc449171 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/HasUseLatestTsConfig.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2026 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.cf.configuration; + +public interface HasUseLatestTsConfig { + + boolean isUseLatestTs(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java index 4c796b25b0..d0ae8789b9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/PropagationCalculatedFieldConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.cf.configuration; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -27,7 +28,7 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) -public class PropagationCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements HasRelationPathLevel { +public class PropagationCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements HasRelationPathLevel, HasUseLatestTsConfig { public static final String PROPAGATION_CONFIG_ARGUMENT = "propagationCtx"; @@ -37,6 +38,12 @@ public class PropagationCalculatedFieldConfiguration extends BaseCalculatedField private boolean applyExpressionToResolvedArguments; + @Override + @JsonIgnore + public boolean isUseLatestTs() { + return output.getType() == OutputType.TIME_SERIES; + } + @Override public CalculatedFieldType getType() { return CalculatedFieldType.PROPAGATION; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java index 00e998ad28..53210d1cf8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; @Data @EqualsAndHashCode(callSuper = true) -public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements ExpressionBasedCalculatedFieldConfiguration { +public class SimpleCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements ExpressionBasedCalculatedFieldConfiguration, HasUseLatestTsConfig { private boolean useLatestTs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java index 3b0df7ed82..d905248aa5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/aggregation/RelatedEntitiesAggregationCalculatedFieldConfiguration.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.HasRelationPathLevel; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.relation.RelationPathLevel; @@ -30,7 +31,7 @@ import org.thingsboard.server.common.data.relation.RelationPathLevel; import java.util.Map; @Data -public class RelatedEntitiesAggregationCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration, HasRelationPathLevel { +public class RelatedEntitiesAggregationCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration, HasRelationPathLevel, HasUseLatestTsConfig { @NotNull private RelationPathLevel relation; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java index 2d1abd0024..cb408c5dbf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/geofencing/GeofencingCalculatedFieldConfiguration.java @@ -22,7 +22,9 @@ import lombok.Data; import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; +import org.thingsboard.server.common.data.cf.configuration.HasUseLatestTsConfig; import org.thingsboard.server.common.data.cf.configuration.Output; +import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.id.EntityId; @@ -35,7 +37,7 @@ import java.util.Set; import static java.util.stream.Collectors.toSet; @Data -public class GeofencingCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration { +public class GeofencingCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration, ScheduledUpdateSupportedCalculatedFieldConfiguration, HasUseLatestTsConfig { @Valid @NotNull @@ -48,8 +50,15 @@ public class GeofencingCalculatedFieldConfiguration implements ArgumentsBasedCal private boolean scheduledUpdateEnabled; private int scheduledUpdateInterval; + @NotNull private Output output; + @Override + @JsonIgnore + public boolean isUseLatestTs() { + return output.getType() == OutputType.TIME_SERIES; + } + @Override public CalculatedFieldType getType() { return CalculatedFieldType.GEOFENCING;