Browse Source

add backend support for dynamic values for schedules in alarm rules

pull/5818/head
desoliture 5 years ago
parent
commit
3351ced60a
  1. 3
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java
  2. 3
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java
  3. 4
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java
  4. 41
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java
  5. 24
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java

3
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CustomTimeSchedule.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.device.profile; package org.thingsboard.server.common.data.device.profile;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.query.DynamicValue;
import java.util.List; import java.util.List;
@ -25,6 +26,8 @@ public class CustomTimeSchedule implements AlarmSchedule {
private String timezone; private String timezone;
private List<CustomTimeScheduleItem> items; private List<CustomTimeScheduleItem> items;
private DynamicValue<String> dynamicValue;
@Override @Override
public AlarmScheduleType getType() { public AlarmScheduleType getType() {
return AlarmScheduleType.CUSTOM; return AlarmScheduleType.CUSTOM;

3
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SpecificTimeSchedule.java

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.device.profile; package org.thingsboard.server.common.data.device.profile;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.query.DynamicValue;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -28,6 +29,8 @@ public class SpecificTimeSchedule implements AlarmSchedule {
private long startsOn; private long startsOn;
private long endsOn; private long endsOn;
private DynamicValue<String> dynamicValue;
@Override @Override
public AlarmScheduleType getType() { public AlarmScheduleType getType() {
return AlarmScheduleType.SPECIFIC_TIME; return AlarmScheduleType.SPECIFIC_TIME;

4
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java

@ -573,6 +573,10 @@ public class JsonConverter {
return JSON_PARSER.parse(json); return JSON_PARSER.parse(json);
} }
public static <T> T parse(String json, Class<T> clazz) {
return fromJson(parse(json), clazz);
}
public static String toJson(JsonElement element) { public static String toJson(JsonElement element) {
return GSON.toJson(element); return GSON.toJson(element);
} }

41
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.query.KeyFilterPredicate;
import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.data.query.StringFilterPredicate;
import org.thingsboard.server.common.msg.tools.SchedulerUtils; import org.thingsboard.server.common.msg.tools.SchedulerUtils;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneId; import java.time.ZoneId;
@ -115,7 +116,7 @@ class AlarmRuleState {
} }
public AlarmEvalResult eval(DataSnapshot data) { public AlarmEvalResult eval(DataSnapshot data) {
boolean active = isActive(data.getTs()); boolean active = isActive(data, data.getTs());
switch (spec.getType()) { switch (spec.getType()) {
case SIMPLE: case SIMPLE:
return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE;
@ -128,7 +129,7 @@ class AlarmRuleState {
} }
} }
private boolean isActive(long eventTs) { private boolean isActive(DataSnapshot data, long eventTs) {
if (eventTs == 0L) { if (eventTs == 0L) {
eventTs = System.currentTimeMillis(); eventTs = System.currentTimeMillis();
} }
@ -138,10 +139,28 @@ class AlarmRuleState {
switch (alarmRule.getSchedule().getType()) { switch (alarmRule.getSchedule().getType()) {
case ANY_TIME: case ANY_TIME:
return true; return true;
case SPECIFIC_TIME: case SPECIFIC_TIME: {
return isActiveSpecific((SpecificTimeSchedule) alarmRule.getSchedule(), eventTs); SpecificTimeSchedule originalSchedule = (SpecificTimeSchedule) alarmRule.getSchedule();
case CUSTOM: EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue());
return isActiveCustom((CustomTimeSchedule) alarmRule.getSchedule(), eventTs);
if (dynamicValue != null) {
SpecificTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), SpecificTimeSchedule.class);
originalSchedule = schedule == null ? originalSchedule : schedule;
}
return isActiveSpecific(originalSchedule, eventTs);
}
case CUSTOM: {
CustomTimeSchedule originalSchedule = (CustomTimeSchedule) alarmRule.getSchedule();
EntityKeyValue dynamicValue = getDynamicValue(data, originalSchedule.getDynamicValue());
if (dynamicValue != null) {
CustomTimeSchedule schedule = JsonConverter.parse(dynamicValue.getJsonValue(), CustomTimeSchedule.class);
originalSchedule = schedule == null ? originalSchedule : schedule;
}
return isActiveCustom(originalSchedule, eventTs);
}
default: default:
throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType()); throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType());
} }
@ -236,7 +255,7 @@ class AlarmRuleState {
if (repeating.getPredicate().getDynamicValue() != null && if (repeating.getPredicate().getDynamicValue() != null &&
repeating.getPredicate().getDynamicValue().getSourceAttribute() != null) { repeating.getPredicate().getDynamicValue().getSourceAttribute() != null) {
EntityKeyValue repeatingKeyValue = getDynamicPredicateValue(data, repeating.getPredicate().getDynamicValue()); EntityKeyValue repeatingKeyValue = getDynamicValue(data, repeating.getPredicate().getDynamicValue());
if (repeatingKeyValue != null) { if (repeatingKeyValue != null) {
repeatingTimes = repeatingKeyValue.getLngValue(); repeatingTimes = repeatingKeyValue.getLngValue();
} }
@ -257,7 +276,7 @@ class AlarmRuleState {
if (duration.getPredicate().getDynamicValue() != null && if (duration.getPredicate().getDynamicValue() != null &&
duration.getPredicate().getDynamicValue().getSourceAttribute() != null) { duration.getPredicate().getDynamicValue().getSourceAttribute() != null) {
EntityKeyValue durationKeyValue = getDynamicPredicateValue(data, duration.getPredicate().getDynamicValue()); EntityKeyValue durationKeyValue = getDynamicValue(data, duration.getPredicate().getDynamicValue());
if (durationKeyValue != null) { if (durationKeyValue != null) {
durationTimeInMs = timeUnit.toMillis(durationKeyValue.getLngValue()); durationTimeInMs = timeUnit.toMillis(durationKeyValue.getLngValue());
} }
@ -276,7 +295,7 @@ class AlarmRuleState {
long requiredDurationInMs = resolveRequiredDurationInMs(dataSnapshot); long requiredDurationInMs = resolveRequiredDurationInMs(dataSnapshot);
if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) {
long duration = state.getDuration() + (ts - state.getLastEventTs()); long duration = state.getDuration() + (ts - state.getLastEventTs());
if (isActive(ts)) { if (isActive(dataSnapshot, ts)) {
return duration > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; return duration > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE;
} else { } else {
return AlarmEvalResult.FALSE; return AlarmEvalResult.FALSE;
@ -443,7 +462,7 @@ class AlarmRuleState {
} }
private <T> T getPredicateValue(DataSnapshot data, FilterPredicateValue<T> value, AlarmConditionFilter filter, Function<EntityKeyValue, T> transformFunction) { private <T> T getPredicateValue(DataSnapshot data, FilterPredicateValue<T> value, AlarmConditionFilter filter, Function<EntityKeyValue, T> transformFunction) {
EntityKeyValue ekv = getDynamicPredicateValue(data, value.getDynamicValue()); EntityKeyValue ekv = getDynamicValue(data, value.getDynamicValue());
if (ekv != null) { if (ekv != null) {
T result = transformFunction.apply(ekv); T result = transformFunction.apply(ekv);
if (result != null) { if (result != null) {
@ -457,7 +476,7 @@ class AlarmRuleState {
} }
} }
private <T> EntityKeyValue getDynamicPredicateValue(DataSnapshot data, DynamicValue<T> value) { private <T> EntityKeyValue getDynamicValue(DataSnapshot data, DynamicValue<T> value) {
EntityKeyValue ekv = null; EntityKeyValue ekv = null;
if (value != null) { if (value != null) {
switch (value.getSourceType()) { switch (value.getSourceType()) {

24
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/ProfileState.java

@ -27,6 +27,9 @@ import org.thingsboard.server.common.data.device.profile.AlarmRule;
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec;
import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec;
import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
import org.thingsboard.server.common.data.device.profile.SpecificTimeSchedule;
import org.thingsboard.server.common.data.device.profile.AlarmSchedule;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.query.ComplexFilterPredicate; import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValue;
@ -77,6 +80,7 @@ class ProfileState {
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys); addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
} }
addEntityKeysFromAlarmConditionSpec(alarmRule); addEntityKeysFromAlarmConditionSpec(alarmRule);
addScheduleDynamicValues(alarmRule.getSchedule());
})); }));
if (alarm.getClearRule() != null) { if (alarm.getClearRule() != null) {
var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>()); var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
@ -91,6 +95,26 @@ class ProfileState {
} }
} }
private void addScheduleDynamicValues(AlarmSchedule schedule) {
DynamicValue<String> dynamicValue = null;
switch (schedule.getType()) {
case SPECIFIC_TIME:
SpecificTimeSchedule specSchedule = (SpecificTimeSchedule) schedule;
dynamicValue = specSchedule.getDynamicValue();
break;
case CUSTOM:
CustomTimeSchedule custSchedule = (CustomTimeSchedule) schedule;
dynamicValue = custSchedule.getDynamicValue();
}
if (dynamicValue != null) {
entityKeys.add(
new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE,
dynamicValue.getSourceAttribute())
);
}
}
private void addEntityKeysFromAlarmConditionSpec(AlarmRule alarmRule) { private void addEntityKeysFromAlarmConditionSpec(AlarmRule alarmRule) {
AlarmConditionSpec spec = alarmRule.getCondition().getSpec(); AlarmConditionSpec spec = alarmRule.getCondition().getSpec();
if (spec == null) { if (spec == null) {

Loading…
Cancel
Save