Browse Source

Alarm rules CF: support for filter predicates

pull/14107/head
VIacheslavKlimov 8 months ago
parent
commit
151dfe4c83
  1. 168
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java
  2. 22
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java
  3. 84
      application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java
  4. 37
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java
  5. 21
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/ComplexOperation.java
  6. 11
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java
  7. 37
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java
  8. 34
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java
  9. 23
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java
  10. 36
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java
  11. 41
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java
  12. 24
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java
  13. 43
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java

168
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmCalculatedFieldState.java

@ -24,6 +24,7 @@ import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.KvUtil;
import org.thingsboard.rule.engine.action.TbAlarmResult;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm;
@ -33,13 +34,23 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.alarm.rule.AlarmRule;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionType;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionFilter;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.SimpleAlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.BooleanFilterPredicate;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.ComplexFilterPredicate;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.KeyFilterPredicate;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.StringFilterPredicate;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.service.cf.AlarmCalculatedFieldResult;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry;
@ -52,6 +63,9 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import static org.thingsboard.server.common.data.StringUtils.equalsAny;
import static org.thingsboard.server.common.data.StringUtils.splitByCommaWithoutQuotes;
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class AlarmCalculatedFieldState extends BaseCalculatedFieldState {
@ -131,20 +145,6 @@ public class AlarmCalculatedFieldState extends BaseCalculatedFieldState {
.build());
}
@SneakyThrows
public boolean eval(AlarmConditionExpression expression, CalculatedFieldCtx ctx) {
if (expression instanceof TbelAlarmConditionExpression tbelExpression) {
Object result = ctx.evaluateTbelExpression(tbelExpression.getExpression(), this).get();
if (result instanceof Boolean booleanResult) {
return booleanResult;
} else {
throw new IllegalStateException("Condition expression returned non-boolean value: '" + result + "'");
}
} else {
throw new UnsupportedOperationException("Simple expressions not supported");
}
}
public void processAlarmAction(Alarm alarm, ActionType action) {
switch (action) {
case ALARM_ACK -> processAlarmAck(alarm);
@ -319,6 +319,146 @@ public class AlarmCalculatedFieldState extends BaseCalculatedFieldState {
return alarmDetails;
}
@SneakyThrows
public boolean eval(AlarmConditionExpression expression, CalculatedFieldCtx ctx) {
if (expression instanceof TbelAlarmConditionExpression tbelExpression) {
Object result = ctx.evaluateTbelExpression(tbelExpression.getExpression(), this).get();
if (result instanceof Boolean booleanResult) {
return booleanResult;
} else {
throw new IllegalStateException("Condition expression returned non-boolean value: '" + result + "'");
}
} else {
SimpleAlarmConditionExpression simpleExpression = (SimpleAlarmConditionExpression) expression;
ComplexOperation operation = simpleExpression.getOperation();
if (operation == null) {
operation = ComplexOperation.AND;
}
return switch (operation) {
case OR -> {
for (AlarmConditionFilter filter : simpleExpression.getFilters()) {
SingleValueArgumentEntry argument = getArgument(filter.getArgument());
if (eval(argument, filter.getPredicate())) {
yield true;
}
}
yield false;
}
case AND -> {
for (AlarmConditionFilter filter : simpleExpression.getFilters()) {
SingleValueArgumentEntry argument = getArgument(filter.getArgument());
if (!eval(argument, filter.getPredicate())) {
yield false;
}
}
yield true;
}
};
}
}
private boolean eval(SingleValueArgumentEntry argument, KeyFilterPredicate predicate) {
return switch (predicate.getType()) {
case STRING -> evalStrPredicate(argument, (StringFilterPredicate) predicate);
case NUMERIC -> evalNumPredicate(argument, (NumericFilterPredicate) predicate);
case BOOLEAN -> evalBooleanPredicate(argument, (BooleanFilterPredicate) predicate);
case COMPLEX -> evalComplexPredicate(argument, (ComplexFilterPredicate) predicate);
};
}
private boolean evalComplexPredicate(SingleValueArgumentEntry argument, ComplexFilterPredicate complexPredicate) {
return switch (complexPredicate.getOperation()) {
case OR -> {
for (KeyFilterPredicate predicate : complexPredicate.getPredicates()) {
if (eval(argument, predicate)) {
yield true;
}
}
yield false;
}
case AND -> {
for (KeyFilterPredicate predicate : complexPredicate.getPredicates()) {
if (!eval(argument, predicate)) {
yield false;
}
}
yield true;
}
};
}
private boolean evalBooleanPredicate(SingleValueArgumentEntry argument, BooleanFilterPredicate predicate) {
Boolean value = KvUtil.getBoolValue(argument.getKvEntryValue());
if (value == null) {
return false;
}
Boolean predicateValue = resolveValue(predicate.getValue(), KvUtil::getBoolValue);
if (predicateValue == null) {
return false;
}
return switch (predicate.getOperation()) {
case EQUAL -> value.equals(predicateValue);
case NOT_EQUAL -> !value.equals(predicateValue);
};
}
private boolean evalNumPredicate(SingleValueArgumentEntry argument, NumericFilterPredicate predicate) {
Double value = KvUtil.getDoubleValue(argument.getKvEntryValue());
if (value == null) {
return false;
}
Double predicateValue = resolveValue(predicate.getValue(), KvUtil::getDoubleValue);
if (predicateValue == null) {
return false;
}
return switch (predicate.getOperation()) {
case NOT_EQUAL -> !value.equals(predicateValue);
case EQUAL -> value.equals(predicateValue);
case GREATER -> value > predicateValue;
case GREATER_OR_EQUAL -> value >= predicateValue;
case LESS -> value < predicateValue;
case LESS_OR_EQUAL -> value <= predicateValue;
};
}
private boolean evalStrPredicate(SingleValueArgumentEntry argument, StringFilterPredicate predicate) {
String value = KvUtil.getStringValue(argument.getKvEntryValue());
if (value == null) {
return false;
}
String predicateValue = resolveValue(predicate.getValue(), KvUtil::getStringValue);
if (predicateValue == null) {
return false;
}
if (predicate.isIgnoreCase()) {
value = value.toLowerCase();
predicateValue = predicateValue.toLowerCase();
}
return switch (predicate.getOperation()) {
case CONTAINS -> value.contains(predicateValue);
case EQUAL -> value.equals(predicateValue);
case STARTS_WITH -> value.startsWith(predicateValue);
case ENDS_WITH -> value.endsWith(predicateValue);
case NOT_EQUAL -> !value.equals(predicateValue);
case NOT_CONTAINS -> !value.contains(predicateValue);
case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
};
}
protected <T> T resolveValue(AlarmConditionValue<T> conditionValue, Function<KvEntry, T> mapper) {
T value = conditionValue.getStaticValue();
if (value == null) {
String argument = conditionValue.getDynamicValueArgument();
SingleValueArgumentEntry entry = getArgument(argument);
value = mapper.apply(entry.getKvEntryValue());
if (value == null) {
throw new IllegalArgumentException("No value found for argument " + argument);
}
}
return value;
}
protected SingleValueArgumentEntry getArgument(String key) {
SingleValueArgumentEntry entry = (SingleValueArgumentEntry) arguments.get(key);
if (entry == null) {

22
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/alarm/AlarmRuleState.java

@ -31,16 +31,13 @@ import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSch
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.CustomTimeSchedule;
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.CustomTimeScheduleItem;
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.SpecificTimeSchedule;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.function.Function;
@Data
@Slf4j
@ -132,7 +129,7 @@ public class AlarmRuleState {
if (condition.getSchedule() == null) {
return true;
}
AlarmSchedule schedule = getValue(condition.getSchedule(), entry -> Optional.ofNullable(KvUtil.getStringValue(entry))
AlarmSchedule schedule = state.resolveValue(condition.getSchedule(), entry -> Optional.ofNullable(KvUtil.getStringValue(entry))
.map(str -> JsonConverter.parse(str, AlarmSchedule.class))
.orElse(null));
return switch (schedule.getType()) {
@ -198,31 +195,18 @@ public class AlarmRuleState {
}
private Integer getIntValue(AlarmConditionValue<Integer> value) {
return getValue(value, entry -> Optional.ofNullable(KvUtil.getLongValue(entry)).map(Long::intValue).orElse(null));
return state.resolveValue(value, entry -> Optional.ofNullable(KvUtil.getLongValue(entry)).map(Long::intValue).orElse(null));
}
private long getRequiredDurationInMs() {
DurationAlarmCondition durationCondition = (DurationAlarmCondition) condition;
return durationCondition.getUnit().toMillis(getValue(durationCondition.getValue(), KvUtil::getLongValue));
return durationCondition.getUnit().toMillis(state.resolveValue(durationCondition.getValue(), KvUtil::getLongValue));
}
private boolean eval(AlarmConditionExpression expression, CalculatedFieldCtx ctx) {
return state.eval(expression, ctx);
}
private <T> T getValue(AlarmConditionValue<T> conditionValue, Function<KvEntry, T> mapper) {
T value = conditionValue.getStaticValue();
if (value == null) {
String argument = conditionValue.getDynamicValueArgument();
SingleValueArgumentEntry entry = state.getArgument(argument);
value = mapper.apply(entry.getKvEntryValue());
if (value == null) {
throw new IllegalArgumentException("No value found for argument " + argument);
}
}
return value;
}
public void setAlarmRule(AlarmRule alarmRule) {
this.alarmRule = alarmRule;
this.condition = alarmRule.getCondition();

84
application/src/test/java/org/thingsboard/server/cf/AlarmRulesTest.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.cf;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.junit.Before;
@ -35,7 +36,13 @@ import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionVal
import org.thingsboard.server.common.data.alarm.rule.condition.DurationAlarmCondition;
import org.thingsboard.server.common.data.alarm.rule.condition.RepeatingAlarmCondition;
import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionFilter;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.SimpleAlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.NumericFilterPredicate.NumericOperation;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration;
@ -134,6 +141,41 @@ public class AlarmRulesTest extends AbstractControllerTest {
});
}
@Test
public void testCreateAlarm_simpleConditionExpression() throws Exception {
Argument temperatureArgument = new Argument();
temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null));
temperatureArgument.setDefaultValue("0");
Map<String, Argument> arguments = Map.of(
"temperature", temperatureArgument
);
SimpleAlarmConditionExpression simpleExpression = new SimpleAlarmConditionExpression();
AlarmConditionFilter filter = new AlarmConditionFilter();
filter.setArgument("temperature");
NumericFilterPredicate predicate = new NumericFilterPredicate();
predicate.setOperation(NumericOperation.GREATER_OR_EQUAL);
AlarmConditionValue<Double> thresholdValue = new AlarmConditionValue<>();
thresholdValue.setStaticValue(100.0);
predicate.setValue(thresholdValue);
filter.setPredicate(predicate);
simpleExpression.setFilters(List.of(filter));
simpleExpression.setOperation(ComplexOperation.AND);
Map<AlarmSeverity, Condition> createRules = Map.of(
AlarmSeverity.CRITICAL, new Condition(simpleExpression, null, null)
);
CalculatedField calculatedField = createAlarmCf(deviceId, "High Temperature Alarm",
arguments, createRules, null);
postTelemetry(deviceId, "{\"temperature\":100}");
checkAlarmResult(calculatedField, alarmResult -> {
assertThat(alarmResult.isCreated()).isTrue();
assertThat(alarmResult.getAlarm().getSeverity()).isEqualTo(AlarmSeverity.CRITICAL);
assertThat(alarmResult.getAlarm().getStatus()).isEqualTo(AlarmStatus.ACTIVE_UNACK);
});
}
/*
* todo: state restore (event count)
* */
@ -310,21 +352,27 @@ public class AlarmRulesTest extends AbstractControllerTest {
private AlarmRule toAlarmRule(Condition condition) {
AlarmRule rule = new AlarmRule();
TbelAlarmConditionExpression expression = new TbelAlarmConditionExpression();
expression.setExpression(condition.expression());
if (condition.eventsCount() != null) {
AlarmConditionExpression expression;
if (condition.getTbelExpression() != null) {
TbelAlarmConditionExpression tbelExpression = new TbelAlarmConditionExpression();
tbelExpression.setExpression(condition.getTbelExpression());
expression = tbelExpression;
} else {
expression = condition.getSimpleExpression();
}
if (condition.getEventsCount() != null) {
RepeatingAlarmCondition alarmCondition = new RepeatingAlarmCondition();
alarmCondition.setExpression(expression);
AlarmConditionValue<Integer> count = new AlarmConditionValue<>();
count.setStaticValue(condition.eventsCount());
count.setStaticValue(condition.getEventsCount());
alarmCondition.setCount(count);
rule.setCondition(alarmCondition);
} else if (condition.durationMs() != null) {
} else if (condition.getDurationMs() != null) {
DurationAlarmCondition alarmCondition = new DurationAlarmCondition();
alarmCondition.setExpression(expression);
alarmCondition.setUnit(TimeUnit.MILLISECONDS);
AlarmConditionValue<Long> duration = new AlarmConditionValue<>();
duration.setStaticValue(condition.durationMs());
duration.setStaticValue(condition.getDurationMs());
alarmCondition.setValue(duration);
rule.setCondition(alarmCondition);
} else {
@ -340,6 +388,28 @@ public class AlarmRulesTest extends AbstractControllerTest {
.map(e -> (CalculatedFieldDebugEvent) e).toList();
}
private record Condition(String expression, Integer eventsCount, Long durationMs) {}
@Getter
private static final class Condition {
private final String tbelExpression;
private final SimpleAlarmConditionExpression simpleExpression;
private final Integer eventsCount;
private final Long durationMs;
private Condition(String tbelExpression, Integer eventsCount, Long durationMs) {
this.tbelExpression = tbelExpression;
this.simpleExpression = null;
this.eventsCount = eventsCount;
this.durationMs = durationMs;
}
private Condition(SimpleAlarmConditionExpression simpleExpression, Integer eventsCount, Long durationMs) {
this.tbelExpression = null;
this.simpleExpression = simpleExpression;
this.eventsCount = eventsCount;
this.durationMs = durationMs;
}
}
}

37
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/AlarmConditionFilter.java

@ -0,0 +1,37 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate.KeyFilterPredicate;
import java.io.Serializable;
@Schema
@Data
public class AlarmConditionFilter implements Serializable {
@NotBlank
private String argument;
@Valid
@NotNull
private KeyFilterPredicate predicate;
}

21
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/ComplexOperation.java

@ -0,0 +1,21 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression;
public enum ComplexOperation {
AND,
OR
}

11
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/SimpleAlarmConditionExpression.java

@ -15,14 +15,19 @@
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class SimpleAlarmConditionExpression implements AlarmConditionExpression {
@NotBlank
private String expression;
@Valid
@NotEmpty
private List<AlarmConditionFilter> filters;
private ComplexOperation operation;
@Override
public AlarmConditionExpressionType getType() {

37
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/BooleanFilterPredicate.java

@ -0,0 +1,37 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Data
public class BooleanFilterPredicate implements SimpleKeyFilterPredicate<Boolean> {
private BooleanOperation operation;
private AlarmConditionValue<Boolean> value;
@Override
public FilterPredicateType getType() {
return FilterPredicateType.BOOLEAN;
}
public enum BooleanOperation {
EQUAL,
NOT_EQUAL
}
}

34
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/ComplexFilterPredicate.java

@ -0,0 +1,34 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.ComplexOperation;
import java.util.List;
@Data
public class ComplexFilterPredicate implements KeyFilterPredicate {
private ComplexOperation operation;
private List<KeyFilterPredicate> predicates;
@Override
public FilterPredicateType getType() {
return FilterPredicateType.COMPLEX;
}
}

23
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/FilterPredicateType.java

@ -0,0 +1,23 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
public enum FilterPredicateType {
STRING,
NUMERIC,
BOOLEAN,
COMPLEX
}

36
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/KeyFilterPredicate.java

@ -0,0 +1,36 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.io.Serializable;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@Type(value = StringFilterPredicate.class, name = "STRING"),
@Type(value = NumericFilterPredicate.class, name = "NUMERIC"),
@Type(value = BooleanFilterPredicate.class, name = "BOOLEAN"),
@Type(value = ComplexFilterPredicate.class, name = "COMPLEX")})
public interface KeyFilterPredicate extends Serializable {
@JsonIgnore
FilterPredicateType getType();
}

41
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/NumericFilterPredicate.java

@ -0,0 +1,41 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Data
public class NumericFilterPredicate implements SimpleKeyFilterPredicate<Double> {
private NumericOperation operation;
private AlarmConditionValue<Double> value;
@Override
public FilterPredicateType getType() {
return FilterPredicateType.NUMERIC;
}
public enum NumericOperation {
EQUAL,
NOT_EQUAL,
GREATER,
LESS,
GREATER_OR_EQUAL,
LESS_OR_EQUAL
}
}

24
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/SimpleKeyFilterPredicate.java

@ -0,0 +1,24 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
public interface SimpleKeyFilterPredicate<T> extends KeyFilterPredicate {
AlarmConditionValue<T> getValue();
}

43
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/predicate/StringFilterPredicate.java

@ -0,0 +1,43 @@
/**
* 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.
*/
package org.thingsboard.server.common.data.alarm.rule.condition.expression.predicate;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmConditionValue;
@Data
public class StringFilterPredicate implements SimpleKeyFilterPredicate<String> {
private StringOperation operation;
private AlarmConditionValue<String> value;
private boolean ignoreCase;
@Override
public FilterPredicateType getType() {
return FilterPredicateType.STRING;
}
public enum StringOperation {
EQUAL,
NOT_EQUAL,
STARTS_WITH,
ENDS_WITH,
CONTAINS,
NOT_CONTAINS,
IN,
NOT_IN
}
}
Loading…
Cancel
Save