diff --git a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java index aef81474ec..fb0f92c2ab 100644 --- a/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java @@ -416,7 +416,7 @@ public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareM } } else { if (DebugModeUtil.isDebugFailuresAvailable(ctx.getCalculatedField())) { - String errorMsg = ctx.isInitialized() ? state.getReadinessStatus().stringValue() : "Calculated field state is not initialized!"; + String errorMsg = ctx.isInitialized() && state.getReadinessStatus() != null ? state.getReadinessStatus().stringValue() : "Calculated field state is not initialized!"; systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, null, errorMsg); } callback.onSuccess(); 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 7272629f80..ac9a6a929b 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 @@ -26,11 +26,11 @@ import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; import org.thingsboard.server.utils.CalculatedFieldUtils; import java.io.Closeable; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; @Getter public abstract class BaseCalculatedFieldState implements CalculatedFieldState, Closeable { @@ -57,7 +57,6 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, this.ctx = ctx; this.actorCtx = actorCtx; this.requiredArguments = ctx.getArgNames(); - this.readinessStatus = ReadinessStatus.initialState(requiredArguments, arguments); } @Override @@ -91,12 +90,15 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, } updatedArguments.put(key, newEntry); updateLastUpdateTimestamp(newEntry); - readinessStatus.onArgumentUpdate(key, newEntry); } } - return Objects.requireNonNullElse(updatedArguments, Collections.emptyMap()); + if (updatedArguments != null) { + readinessStatus = checkReadiness(requiredArguments, arguments); + return updatedArguments; + } + return Collections.emptyMap(); } @Override @@ -109,7 +111,7 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, @Override public boolean isReady() { - return readinessStatus.isReady(); + return readinessStatus != null && readinessStatus.isReady(); } @Override @@ -150,4 +152,18 @@ public abstract class BaseCalculatedFieldState implements CalculatedFieldState, this.latestTimestamp = Math.max(this.latestTimestamp, newTs); } + public ReadinessStatus checkReadiness(List requiredArguments, Map currentArguments) { + List emptyArguments = null; + for (String requiredArgumentKey : requiredArguments) { + ArgumentEntry argumentEntry = currentArguments.get(requiredArgumentKey); + if (argumentEntry == null || argumentEntry.isEmpty()) { + if (emptyArguments == null) { + emptyArguments = new ArrayList<>(); + } + emptyArguments.add(requiredArgumentKey); + } + } + return new ReadinessStatus(emptyArguments); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java index 28fa70e77c..ef3139396a 100644 --- a/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java +++ b/application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java @@ -36,10 +36,8 @@ import org.thingsboard.server.service.cf.ctx.state.geofencing.GeofencingCalculat import org.thingsboard.server.service.cf.ctx.state.propagation.PropagationCalculatedFieldState; import java.io.Closeable; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import static org.thingsboard.server.utils.CalculatedFieldUtils.toSingleValueArgumentProto; @@ -103,52 +101,20 @@ public interface CalculatedFieldState extends Closeable { @Data @AllArgsConstructor - @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonInclude(JsonInclude.Include.NON_NULL) class ReadinessStatus { - private Set missingArguments; - private Set emptyArguments; - - public static ReadinessStatus initialState(List requiredArguments, Map arguments) { - if (arguments.isEmpty()) { - return new ReadinessStatus(new HashSet<>(requiredArguments), new HashSet<>()); - } - Set missingArguments = new HashSet<>(requiredArguments.size()); - Set emptyArguments = new HashSet<>(requiredArguments.size()); - requiredArguments.forEach(requiredArgumentKey -> { - ArgumentEntry argumentEntry = arguments.get(requiredArgumentKey); - if (argumentEntry == null) { - missingArguments.add(requiredArgumentKey); - return; - } - if (argumentEntry.isEmpty()) { - emptyArguments.add(requiredArgumentKey); - } - }); - return new ReadinessStatus(missingArguments, emptyArguments); - } - - public void onArgumentUpdate(String key, ArgumentEntry newEntry) { - if (newEntry == null) { - missingArguments.add(key); - return; - } - missingArguments.remove(key); - if (newEntry.isEmpty()) { - emptyArguments.add(key); - } else { - emptyArguments.remove(key); - } - } + private List emptyArguments; public boolean isReady() { - return missingArguments.isEmpty() && emptyArguments.isEmpty(); + return emptyArguments == null || emptyArguments.isEmpty(); } public String stringValue() { return JacksonUtil.toString(this); } + } } 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 db708b51af..cc22797593 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 @@ -50,7 +50,6 @@ public class PropagationCalculatedFieldState extends ScriptCalculatedFieldState this.actorCtx = actorCtx; this.requiredArguments = new ArrayList<>(ctx.getArgNames()); requiredArguments.add(PROPAGATION_CONFIG_ARGUMENT); - this.readinessStatus = ReadinessStatus.initialState(requiredArguments, arguments); if (ctx.isApplyExpressionForResolvedArguments()) { this.tbelExpression = ctx.getTbelExpressions().get(ctx.getExpression()); } diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java index 5e304e7442..a91834a798 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/GeofencingCalculatedFieldStateTest.java @@ -200,9 +200,7 @@ public class GeofencingCalculatedFieldStateTest { @Test void testIsReadyWhenNotAllArgPresent() { assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()) - .containsExactlyInAnyOrderElementsOf(state.requiredArguments); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus()).isNull(); } @Test @@ -214,8 +212,7 @@ public class GeofencingCalculatedFieldStateTest { "restrictedZones", geofencingRestrictedZoneArgEntry ), ctx); assertThat(state.isReady()).isTrue(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus().getEmptyArguments()).isNull(); } @Test @@ -227,7 +224,6 @@ public class GeofencingCalculatedFieldStateTest { "restrictedZones", new GeofencingArgumentEntry() ), ctx); assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); assertThat(state.getReadinessStatus().getEmptyArguments()).contains("restrictedZones"); } diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java index 11ec6031a5..a03be03f59 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/PropagationCalculatedFieldStateTest.java @@ -131,7 +131,7 @@ public class PropagationCalculatedFieldStateTest { initCtxAndState(false); state.update(Map.of(TEMPERATURE_ARGUMENT_NAME, singleValueArgEntry), ctx); assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()).containsExactly(PROPAGATION_CONFIG_ARGUMENT); + assertThat(state.getReadinessStatus().getEmptyArguments()).containsExactly(PROPAGATION_CONFIG_ARGUMENT); } @Test @@ -139,9 +139,8 @@ public class PropagationCalculatedFieldStateTest { initCtxAndState(false); state.update(Map.of(TEMPERATURE_ARGUMENT_NAME, singleValueArgEntry, PROPAGATION_CONFIG_ARGUMENT, new PropagationArgumentEntry(Collections.emptyList())), ctx); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); - assertThat(state.getReadinessStatus().getEmptyArguments()).containsExactly(PROPAGATION_CONFIG_ARGUMENT); assertThat(state.isReady()).isFalse(); + assertThat(state.getReadinessStatus().getEmptyArguments()).containsExactly(PROPAGATION_CONFIG_ARGUMENT); } @Test @@ -149,8 +148,7 @@ public class PropagationCalculatedFieldStateTest { initCtxAndState(false); state.update(Map.of(TEMPERATURE_ARGUMENT_NAME, singleValueArgEntry, PROPAGATION_CONFIG_ARGUMENT, propagationArgEntry), ctx); assertThat(state.isReady()).isTrue(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus().getEmptyArguments()).isNull(); } diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java index 283b321587..a8dcc18025 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldStateTest.java @@ -161,17 +161,14 @@ public class ScriptCalculatedFieldStateTest { @Test void testIsReadyWhenNotAllArgPresent() { assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()) - .containsExactlyInAnyOrderElementsOf(state.requiredArguments); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus()).isNull(); } @Test void testIsReadyWhenAllArgPresent() { state.update(Map.of("deviceTemperature", deviceTemperatureArgEntry, "assetHumidity", assetHumidityArgEntry), ctx); assertThat(state.isReady()).isTrue(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus().getEmptyArguments()).isNull(); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java index 69674dc370..7748186919 100644 --- a/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java +++ b/application/src/test/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldStateTest.java @@ -204,9 +204,7 @@ public class SimpleCalculatedFieldStateTest { @Test void testIsReadyWhenNotAllArgPresent() { assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()) - .containsExactlyInAnyOrderElementsOf(state.requiredArguments); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus()).isNull(); } @Test @@ -217,8 +215,7 @@ public class SimpleCalculatedFieldStateTest { "key3", key3ArgEntry ), ctx); assertThat(state.isReady()).isTrue(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); - assertThat(state.getReadinessStatus().getEmptyArguments()).isEmpty(); + assertThat(state.getReadinessStatus().getEmptyArguments()).isNull(); } @Test @@ -229,7 +226,6 @@ public class SimpleCalculatedFieldStateTest { "key3", new SingleValueArgumentEntry() ), ctx); assertThat(state.isReady()).isFalse(); - assertThat(state.getReadinessStatus().getMissingArguments()).isEmpty(); assertThat(state.getReadinessStatus().getEmptyArguments()).containsExactly("key3"); }