Constant filters for device profile
This commit is contained in:
parent
7237497946
commit
3cd964327a
@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile;
|
|||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||||
@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
AlarmCondition temperatureCondition = new AlarmCondition();
|
AlarmCondition temperatureCondition = new AlarmCondition();
|
||||||
temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter();
|
AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter();
|
||||||
temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
|
temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
|
||||||
temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
||||||
temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||||
temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
|
temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
|
||||||
|
|
||||||
KeyFilter temperatureTimeseriesFilter = new KeyFilter();
|
AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter();
|
||||||
temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
AlarmCondition clearTemperatureCondition = new AlarmCondition();
|
AlarmCondition clearTemperatureCondition = new AlarmCondition();
|
||||||
clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();
|
AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter();
|
||||||
clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
|
clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
|
||||||
@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
AlarmCondition humidityCondition = new AlarmCondition();
|
AlarmCondition humidityCondition = new AlarmCondition();
|
||||||
humidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
humidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter();
|
AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter();
|
||||||
humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag"));
|
humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag"));
|
||||||
humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
||||||
humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||||
humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
|
humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
|
||||||
|
|
||||||
KeyFilter humidityTimeseriesFilter = new KeyFilter();
|
AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter();
|
||||||
humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
|
||||||
humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||||
@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
AlarmCondition clearHumidityCondition = new AlarmCondition();
|
AlarmCondition clearHumidityCondition = new AlarmCondition();
|
||||||
clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();
|
AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter();
|
||||||
clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
|
||||||
clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
|
clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class AlarmCondition {
|
public class AlarmCondition {
|
||||||
|
|
||||||
private List<KeyFilter> condition;
|
private List<AlarmConditionFilter> condition;
|
||||||
private AlarmConditionSpec spec;
|
private AlarmConditionSpec spec;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2021 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.device.profile;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKeyValueType;
|
||||||
|
import org.thingsboard.server.common.data.query.KeyFilterPredicate;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AlarmConditionFilter {
|
||||||
|
|
||||||
|
private AlarmConditionFilterKey key;
|
||||||
|
private EntityKeyValueType valueType;
|
||||||
|
private Object value;
|
||||||
|
private KeyFilterPredicate predicate;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2021 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.device.profile;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AlarmConditionFilterKey {
|
||||||
|
|
||||||
|
private final AlarmConditionKeyType type;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2021 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.device.profile;
|
||||||
|
|
||||||
|
public enum AlarmConditionKeyType {
|
||||||
|
ATTRIBUTE,
|
||||||
|
TIME_SERIES,
|
||||||
|
ENTITY_FIELD,
|
||||||
|
CONSTANT
|
||||||
|
}
|
||||||
@ -16,9 +16,13 @@
|
|||||||
package org.thingsboard.rule.engine.profile;
|
package org.thingsboard.rule.engine.profile;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
|
import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
||||||
import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
|
import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
|
||||||
@ -45,6 +49,7 @@ import java.util.Set;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@Slf4j
|
||||||
class AlarmRuleState {
|
class AlarmRuleState {
|
||||||
|
|
||||||
private final AlarmSeverity severity;
|
private final AlarmSeverity severity;
|
||||||
@ -52,12 +57,12 @@ class AlarmRuleState {
|
|||||||
private final AlarmConditionSpec spec;
|
private final AlarmConditionSpec spec;
|
||||||
private final long requiredDurationInMs;
|
private final long requiredDurationInMs;
|
||||||
private final long requiredRepeats;
|
private final long requiredRepeats;
|
||||||
private final Set<EntityKey> entityKeys;
|
private final Set<AlarmConditionFilterKey> entityKeys;
|
||||||
private PersistedAlarmRuleState state;
|
private PersistedAlarmRuleState state;
|
||||||
private boolean updateFlag;
|
private boolean updateFlag;
|
||||||
private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
|
private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
|
||||||
|
|
||||||
AlarmRuleState(AlarmSeverity severity, AlarmRule alarmRule, Set<EntityKey> entityKeys, PersistedAlarmRuleState state, DynamicPredicateValueCtx dynamicPredicateValueCtx) {
|
AlarmRuleState(AlarmSeverity severity, AlarmRule alarmRule, Set<AlarmConditionFilterKey> entityKeys, PersistedAlarmRuleState state, DynamicPredicateValueCtx dynamicPredicateValueCtx) {
|
||||||
this.severity = severity;
|
this.severity = severity;
|
||||||
this.alarmRule = alarmRule;
|
this.alarmRule = alarmRule;
|
||||||
this.entityKeys = entityKeys;
|
this.entityKeys = entityKeys;
|
||||||
@ -84,8 +89,8 @@ class AlarmRuleState {
|
|||||||
this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;
|
this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateTsUpdate(Set<EntityKey> changedKeys) {
|
public boolean validateTsUpdate(Set<AlarmConditionFilterKey> changedKeys) {
|
||||||
for (EntityKey key : changedKeys) {
|
for (AlarmConditionFilterKey key : changedKeys) {
|
||||||
if (entityKeys.contains(key)) {
|
if (entityKeys.contains(key)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -93,18 +98,14 @@ class AlarmRuleState {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateAttrUpdate(Set<EntityKey> changedKeys) {
|
public boolean validateAttrUpdate(Set<AlarmConditionFilterKey> changedKeys) {
|
||||||
//If the attribute was updated, but no new telemetry arrived - we ignore this until new telemetry is there.
|
//If the attribute was updated, but no new telemetry arrived - we ignore this until new telemetry is there.
|
||||||
for (EntityKey key : entityKeys) {
|
for (AlarmConditionFilterKey key : entityKeys) {
|
||||||
if (key.getType().equals(EntityKeyType.TIME_SERIES)) {
|
if (key.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (EntityKey key : changedKeys) {
|
for (AlarmConditionFilterKey key : changedKeys) {
|
||||||
EntityKeyType keyType = key.getType();
|
|
||||||
if (EntityKeyType.CLIENT_ATTRIBUTE.equals(keyType) || EntityKeyType.SERVER_ATTRIBUTE.equals(keyType) || EntityKeyType.SHARED_ATTRIBUTE.equals(keyType)) {
|
|
||||||
key = new EntityKey(EntityKeyType.ATTRIBUTE, key.getKey());
|
|
||||||
}
|
|
||||||
if (entityKeys.contains(key)) {
|
if (entityKeys.contains(key)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -259,16 +260,46 @@ class AlarmRuleState {
|
|||||||
|
|
||||||
private boolean eval(AlarmCondition condition, DataSnapshot data) {
|
private boolean eval(AlarmCondition condition, DataSnapshot data) {
|
||||||
boolean eval = true;
|
boolean eval = true;
|
||||||
for (KeyFilter keyFilter : condition.getCondition()) {
|
for (var filter : condition.getCondition()) {
|
||||||
EntityKeyValue value = data.getValue(keyFilter.getKey());
|
EntityKeyValue value;
|
||||||
|
if (filter.getKey().getType().equals(AlarmConditionKeyType.CONSTANT)) {
|
||||||
|
try {
|
||||||
|
value = getConstantValue(filter);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
log.warn("Failed to parse constant value from filter: {}", filter, e);
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = data.getValue(filter.getKey());
|
||||||
|
}
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
eval = eval && eval(data, value, keyFilter.getPredicate());
|
eval = eval && eval(data, value, filter.getPredicate());
|
||||||
}
|
}
|
||||||
return eval;
|
return eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EntityKeyValue getConstantValue(AlarmConditionFilter filter) {
|
||||||
|
EntityKeyValue value = new EntityKeyValue();
|
||||||
|
String valueStr = filter.getValue().toString();
|
||||||
|
switch (filter.getValueType()) {
|
||||||
|
case STRING:
|
||||||
|
value.setStrValue(valueStr);
|
||||||
|
break;
|
||||||
|
case DATE_TIME:
|
||||||
|
value.setLngValue(Long.valueOf(valueStr));
|
||||||
|
break;
|
||||||
|
case NUMERIC:
|
||||||
|
value.setDblValue(Double.valueOf(valueStr));
|
||||||
|
break;
|
||||||
|
case BOOLEAN:
|
||||||
|
value.setBoolValue(Boolean.valueOf(valueStr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) {
|
private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) {
|
||||||
switch (predicate.getType()) {
|
switch (predicate.getType()) {
|
||||||
case STRING:
|
case STRING:
|
||||||
@ -389,22 +420,13 @@ class AlarmRuleState {
|
|||||||
if (value.getDynamicValue() != null) {
|
if (value.getDynamicValue() != null) {
|
||||||
switch (value.getDynamicValue().getSourceType()) {
|
switch (value.getDynamicValue().getSourceType()) {
|
||||||
case CURRENT_DEVICE:
|
case CURRENT_DEVICE:
|
||||||
ekv = data.getValue(new EntityKey(EntityKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
ekv = data.getValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
||||||
if (ekv == null) {
|
if (ekv != null || !value.getDynamicValue().isInherit()) {
|
||||||
ekv = data.getValue(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
|
||||||
if (ekv == null) {
|
|
||||||
ekv = data.getValue(new EntityKey(EntityKeyType.SHARED_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
|
||||||
if (ekv == null) {
|
|
||||||
ekv = data.getValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ekv != null || !value.getDynamicValue().isInherit()) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CURRENT_CUSTOMER:
|
case CURRENT_CUSTOMER:
|
||||||
ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
|
ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
|
||||||
if(ekv != null || !value.getDynamicValue().isInherit()) {
|
if (ekv != null || !value.getDynamicValue().isInherit()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CURRENT_TENANT:
|
case CURRENT_TENANT:
|
||||||
|
|||||||
@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Tenant;
|
|||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
import org.thingsboard.server.common.data.alarm.AlarmStatus;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
@ -138,9 +139,9 @@ class AlarmState {
|
|||||||
public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
|
public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
|
||||||
if (update != null) {
|
if (update != null) {
|
||||||
//Check that the update type and that keys match.
|
//Check that the update type and that keys match.
|
||||||
if (update.getType().equals(EntityKeyType.TIME_SERIES)) {
|
if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
|
||||||
return state.validateTsUpdate(update.getKeys());
|
return state.validateTsUpdate(update.getKeys());
|
||||||
} else if (update.getType().equals(EntityKeyType.ATTRIBUTE)) {
|
} else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
|
||||||
return state.validateAttrUpdate(update.getKeys());
|
return state.validateAttrUpdate(update.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,7 +253,7 @@ class AlarmState {
|
|||||||
String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
|
String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(alarmDetailsStr)) {
|
if (StringUtils.isNotEmpty(alarmDetailsStr)) {
|
||||||
for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
|
for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
|
||||||
EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
|
EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
|
||||||
alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue));
|
alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,8 @@ package org.thingsboard.rule.engine.profile;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.query.EntityKey;
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
import org.thingsboard.server.common.data.query.EntityKeyType;
|
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||||
|
|
||||||
@ -30,55 +32,42 @@ class DataSnapshot {
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private long ts;
|
private long ts;
|
||||||
private final Set<EntityKey> keys;
|
private final Set<AlarmConditionFilterKey> keys;
|
||||||
private final Map<EntityKey, EntityKeyValue> values = new ConcurrentHashMap<>();
|
private final Map<AlarmConditionFilterKey, EntityKeyValue> values = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
DataSnapshot(Set<EntityKey> entityKeysToFetch) {
|
DataSnapshot(Set<AlarmConditionFilterKey> entityKeysToFetch) {
|
||||||
this.keys = entityKeysToFetch;
|
this.keys = entityKeysToFetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AlarmConditionFilterKey toConditionKey(EntityKey key) {
|
||||||
|
return new AlarmConditionFilterKey(toConditionKeyType(key.getType()), key.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
static AlarmConditionKeyType toConditionKeyType(EntityKeyType keyType) {
|
||||||
|
switch (keyType) {
|
||||||
|
case ATTRIBUTE:
|
||||||
|
case SERVER_ATTRIBUTE:
|
||||||
|
case SHARED_ATTRIBUTE:
|
||||||
|
case CLIENT_ATTRIBUTE:
|
||||||
|
return AlarmConditionKeyType.ATTRIBUTE;
|
||||||
|
case TIME_SERIES:
|
||||||
|
return AlarmConditionKeyType.TIME_SERIES;
|
||||||
|
case ENTITY_FIELD:
|
||||||
|
return AlarmConditionKeyType.ENTITY_FIELD;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Not supported entity key: " + keyType.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void removeValue(EntityKey key) {
|
void removeValue(EntityKey key) {
|
||||||
switch (key.getType()) {
|
values.remove(toConditionKey(key));
|
||||||
case ATTRIBUTE:
|
|
||||||
values.remove(key);
|
|
||||||
values.remove(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE));
|
|
||||||
values.remove(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE));
|
|
||||||
values.remove(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE));
|
|
||||||
break;
|
|
||||||
case CLIENT_ATTRIBUTE:
|
|
||||||
case SHARED_ATTRIBUTE:
|
|
||||||
case SERVER_ATTRIBUTE:
|
|
||||||
values.remove(key);
|
|
||||||
values.remove(getAttrKey(key, EntityKeyType.ATTRIBUTE));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
values.remove(key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean putValue(EntityKey key, long newTs, EntityKeyValue value) {
|
boolean putValue(AlarmConditionFilterKey key, long newTs, EntityKeyValue value) {
|
||||||
boolean updateOfTs = ts != newTs;
|
return putIfKeyExists(key, value, ts != newTs);
|
||||||
boolean result = false;
|
|
||||||
switch (key.getType()) {
|
|
||||||
case ATTRIBUTE:
|
|
||||||
result |= putIfKeyExists(key, value, updateOfTs);
|
|
||||||
result |= putIfKeyExists(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE), value, updateOfTs);
|
|
||||||
result |= putIfKeyExists(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE), value, updateOfTs);
|
|
||||||
result |= putIfKeyExists(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE), value, updateOfTs);
|
|
||||||
break;
|
|
||||||
case CLIENT_ATTRIBUTE:
|
|
||||||
case SHARED_ATTRIBUTE:
|
|
||||||
case SERVER_ATTRIBUTE:
|
|
||||||
result |= putIfKeyExists(key, value, updateOfTs);
|
|
||||||
result |= putIfKeyExists(getAttrKey(key, EntityKeyType.ATTRIBUTE), value, updateOfTs);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
result |= putIfKeyExists(key, value, updateOfTs);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean putIfKeyExists(EntityKey key, EntityKeyValue value, boolean updateOfTs) {
|
private boolean putIfKeyExists(AlarmConditionFilterKey key, EntityKeyValue value, boolean updateOfTs) {
|
||||||
if (keys.contains(key)) {
|
if (keys.contains(key)) {
|
||||||
EntityKeyValue oldValue = values.put(key, value);
|
EntityKeyValue oldValue = values.put(key, value);
|
||||||
if (updateOfTs) {
|
if (updateOfTs) {
|
||||||
@ -91,25 +80,7 @@ class DataSnapshot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityKeyValue getValue(EntityKey key) {
|
EntityKeyValue getValue(AlarmConditionFilterKey key) {
|
||||||
if (EntityKeyType.ATTRIBUTE.equals(key.getType())) {
|
|
||||||
EntityKeyValue value = values.get(key);
|
|
||||||
if (value == null) {
|
|
||||||
value = values.get(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE));
|
|
||||||
if (value == null) {
|
|
||||||
value = values.get(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE));
|
|
||||||
if (value == null) {
|
|
||||||
value = values.get(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
} else {
|
|
||||||
return values.get(key);
|
return values.get(key);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private EntityKey getAttrKey(EntityKey key, EntityKeyType clientAttribute) {
|
|
||||||
return new EntityKey(clientAttribute, key.getKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.DataConstants;
|
|||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
@ -97,10 +99,10 @@ class DeviceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateProfile(TbContext ctx, DeviceProfile deviceProfile) throws ExecutionException, InterruptedException {
|
public void updateProfile(TbContext ctx, DeviceProfile deviceProfile) throws ExecutionException, InterruptedException {
|
||||||
Set<EntityKey> oldKeys = this.deviceProfile.getEntityKeys();
|
Set<AlarmConditionFilterKey> oldKeys = this.deviceProfile.getEntityKeys();
|
||||||
this.deviceProfile.updateDeviceProfile(deviceProfile);
|
this.deviceProfile.updateDeviceProfile(deviceProfile);
|
||||||
if (latestValues != null) {
|
if (latestValues != null) {
|
||||||
Set<EntityKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
|
Set<AlarmConditionFilterKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
|
||||||
keysToFetch.removeAll(oldKeys);
|
keysToFetch.removeAll(oldKeys);
|
||||||
if (!keysToFetch.isEmpty()) {
|
if (!keysToFetch.isEmpty()) {
|
||||||
addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
|
addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
|
||||||
@ -260,29 +262,29 @@ class DeviceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) {
|
private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) {
|
||||||
Set<EntityKey> keys = new HashSet<>();
|
Set<AlarmConditionFilterKey> keys = new HashSet<>();
|
||||||
for (KvEntry entry : data) {
|
for (KvEntry entry : data) {
|
||||||
EntityKey entityKey = new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey());
|
AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey());
|
||||||
if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
|
if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
|
||||||
keys.add(entityKey);
|
keys.add(entityKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
latestValues.setTs(newTs);
|
latestValues.setTs(newTs);
|
||||||
return new SnapshotUpdate(EntityKeyType.TIME_SERIES, keys);
|
return new SnapshotUpdate(AlarmConditionKeyType.TIME_SERIES, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SnapshotUpdate merge(DataSnapshot latestValues, Set<AttributeKvEntry> attributes, String scope) {
|
private SnapshotUpdate merge(DataSnapshot latestValues, Set<AttributeKvEntry> attributes, String scope) {
|
||||||
long newTs = 0;
|
long newTs = 0;
|
||||||
Set<EntityKey> keys = new HashSet<>();
|
Set<AlarmConditionFilterKey> keys = new HashSet<>();
|
||||||
for (AttributeKvEntry entry : attributes) {
|
for (AttributeKvEntry entry : attributes) {
|
||||||
newTs = Math.max(newTs, entry.getLastUpdateTs());
|
newTs = Math.max(newTs, entry.getLastUpdateTs());
|
||||||
EntityKey entityKey = new EntityKey(getKeyTypeFromScope(scope), entry.getKey());
|
AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey());
|
||||||
if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
|
if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
|
||||||
keys.add(entityKey);
|
keys.add(entityKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
latestValues.setTs(newTs);
|
latestValues.setTs(newTs);
|
||||||
return new SnapshotUpdate(EntityKeyType.ATTRIBUTE, keys);
|
return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EntityKeyType getKeyTypeFromScope(String scope) {
|
private static EntityKeyType getKeyTypeFromScope(String scope) {
|
||||||
@ -298,37 +300,22 @@ class DeviceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException {
|
private DataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException {
|
||||||
Set<EntityKey> entityKeysToFetch = deviceProfile.getEntityKeys();
|
Set<AlarmConditionFilterKey> entityKeysToFetch = deviceProfile.getEntityKeys();
|
||||||
DataSnapshot result = new DataSnapshot(entityKeysToFetch);
|
DataSnapshot result = new DataSnapshot(entityKeysToFetch);
|
||||||
addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
|
addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<EntityKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {
|
private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<AlarmConditionFilterKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {
|
||||||
Set<String> serverAttributeKeys = new HashSet<>();
|
Set<String> attributeKeys = new HashSet<>();
|
||||||
Set<String> clientAttributeKeys = new HashSet<>();
|
|
||||||
Set<String> sharedAttributeKeys = new HashSet<>();
|
|
||||||
Set<String> commonAttributeKeys = new HashSet<>();
|
|
||||||
Set<String> latestTsKeys = new HashSet<>();
|
Set<String> latestTsKeys = new HashSet<>();
|
||||||
|
|
||||||
Device device = null;
|
Device device = null;
|
||||||
for (EntityKey entityKey : entityKeysToFetch) {
|
for (AlarmConditionFilterKey entityKey : entityKeysToFetch) {
|
||||||
String key = entityKey.getKey();
|
String key = entityKey.getKey();
|
||||||
switch (entityKey.getType()) {
|
switch (entityKey.getType()) {
|
||||||
case SERVER_ATTRIBUTE:
|
|
||||||
serverAttributeKeys.add(key);
|
|
||||||
break;
|
|
||||||
case CLIENT_ATTRIBUTE:
|
|
||||||
clientAttributeKeys.add(key);
|
|
||||||
break;
|
|
||||||
case SHARED_ATTRIBUTE:
|
|
||||||
sharedAttributeKeys.add(key);
|
|
||||||
break;
|
|
||||||
case ATTRIBUTE:
|
case ATTRIBUTE:
|
||||||
serverAttributeKeys.add(key);
|
attributeKeys.add(key);
|
||||||
clientAttributeKeys.add(key);
|
|
||||||
sharedAttributeKeys.add(key);
|
|
||||||
commonAttributeKeys.add(key);
|
|
||||||
break;
|
break;
|
||||||
case TIME_SERIES:
|
case TIME_SERIES:
|
||||||
latestTsKeys.add(key);
|
latestTsKeys.add(key);
|
||||||
@ -361,32 +348,22 @@ class DeviceState {
|
|||||||
List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get();
|
List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get();
|
||||||
for (TsKvEntry entry : data) {
|
for (TsKvEntry entry : data) {
|
||||||
if (entry.getValue() != null) {
|
if (entry.getValue() != null) {
|
||||||
result.putValue(new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey()), entry.getTs(), toEntityValue(entry));
|
result.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey()), entry.getTs(), toEntityValue(entry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!clientAttributeKeys.isEmpty()) {
|
if (!attributeKeys.isEmpty()) {
|
||||||
addToSnapshot(result, commonAttributeKeys,
|
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, attributeKeys).get());
|
||||||
ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, clientAttributeKeys).get());
|
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SHARED_SCOPE, attributeKeys).get());
|
||||||
}
|
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, attributeKeys).get());
|
||||||
if (!sharedAttributeKeys.isEmpty()) {
|
|
||||||
addToSnapshot(result, commonAttributeKeys,
|
|
||||||
ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SHARED_SCOPE, sharedAttributeKeys).get());
|
|
||||||
}
|
|
||||||
if (!serverAttributeKeys.isEmpty()) {
|
|
||||||
addToSnapshot(result, commonAttributeKeys,
|
|
||||||
ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, serverAttributeKeys).get());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToSnapshot(DataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) {
|
private void addToSnapshot(DataSnapshot snapshot, List<AttributeKvEntry> data) {
|
||||||
for (AttributeKvEntry entry : data) {
|
for (AttributeKvEntry entry : data) {
|
||||||
if (entry.getValue() != null) {
|
if (entry.getValue() != null) {
|
||||||
EntityKeyValue value = toEntityValue(entry);
|
EntityKeyValue value = toEntityValue(entry);
|
||||||
snapshot.putValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
snapshot.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
||||||
if (commonAttributeKeys.contains(entry.getKey())) {
|
|
||||||
snapshot.putValue(new EntityKey(EntityKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,9 @@ import lombok.AccessLevel;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
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.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
@ -50,10 +53,10 @@ class ProfileState {
|
|||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>();
|
private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>();
|
||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
private final Set<EntityKey> entityKeys = ConcurrentHashMap.newKeySet();
|
private final Set<AlarmConditionFilterKey> entityKeys = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
private final Map<String, Map<AlarmSeverity, Set<EntityKey>>> alarmCreateKeys = new HashMap<>();
|
private final Map<String, Map<AlarmSeverity, Set<AlarmConditionFilterKey>>> alarmCreateKeys = new HashMap<>();
|
||||||
private final Map<String, Set<EntityKey>> alarmClearKeys = new HashMap<>();
|
private final Map<String, Set<AlarmConditionFilterKey>> alarmClearKeys = new HashMap<>();
|
||||||
|
|
||||||
ProfileState(DeviceProfile deviceProfile) {
|
ProfileState(DeviceProfile deviceProfile) {
|
||||||
updateDeviceProfile(deviceProfile);
|
updateDeviceProfile(deviceProfile);
|
||||||
@ -68,18 +71,18 @@ class ProfileState {
|
|||||||
if (deviceProfile.getProfileData().getAlarms() != null) {
|
if (deviceProfile.getProfileData().getAlarms() != null) {
|
||||||
alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
|
alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
|
||||||
for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
|
for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
|
||||||
Map<AlarmSeverity, Set<EntityKey>> createAlarmKeys = alarmCreateKeys.computeIfAbsent(alarm.getId(), id -> new HashMap<>());
|
Map<AlarmSeverity, Set<AlarmConditionFilterKey>> createAlarmKeys = alarmCreateKeys.computeIfAbsent(alarm.getId(), id -> new HashMap<>());
|
||||||
alarm.getCreateRules().forEach(((severity, alarmRule) -> {
|
alarm.getCreateRules().forEach(((severity, alarmRule) -> {
|
||||||
Set<EntityKey> ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());
|
var ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());
|
||||||
for (KeyFilter keyFilter : alarmRule.getCondition().getCondition()) {
|
for (var keyFilter : alarmRule.getCondition().getCondition()) {
|
||||||
entityKeys.add(keyFilter.getKey());
|
entityKeys.add(keyFilter.getKey());
|
||||||
ruleKeys.add(keyFilter.getKey());
|
ruleKeys.add(keyFilter.getKey());
|
||||||
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
|
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
if (alarm.getClearRule() != null) {
|
if (alarm.getClearRule() != null) {
|
||||||
Set<EntityKey> clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
|
var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
|
||||||
for (KeyFilter keyFilter : alarm.getClearRule().getCondition().getCondition()) {
|
for (var keyFilter : alarm.getClearRule().getCondition().getCondition()) {
|
||||||
entityKeys.add(keyFilter.getKey());
|
entityKeys.add(keyFilter.getKey());
|
||||||
clearAlarmKeys.add(keyFilter.getKey());
|
clearAlarmKeys.add(keyFilter.getKey());
|
||||||
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys);
|
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys);
|
||||||
@ -89,7 +92,7 @@ class ProfileState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<EntityKey> entityKeys, Set<EntityKey> ruleKeys) {
|
private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<AlarmConditionFilterKey> entityKeys, Set<AlarmConditionFilterKey> ruleKeys) {
|
||||||
switch (predicate.getType()) {
|
switch (predicate.getType()) {
|
||||||
case STRING:
|
case STRING:
|
||||||
case NUMERIC:
|
case NUMERIC:
|
||||||
@ -98,7 +101,7 @@ class ProfileState {
|
|||||||
if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
|
if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
|
||||||
value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
|
value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
|
||||||
value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) {
|
value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) {
|
||||||
EntityKey entityKey = new EntityKey(EntityKeyType.ATTRIBUTE, value.getSourceAttribute());
|
AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getSourceAttribute());
|
||||||
entityKeys.add(entityKey);
|
entityKeys.add(entityKey);
|
||||||
ruleKeys.add(entityKey);
|
ruleKeys.add(entityKey);
|
||||||
}
|
}
|
||||||
@ -115,12 +118,12 @@ class ProfileState {
|
|||||||
return deviceProfile.getId();
|
return deviceProfile.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<EntityKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {
|
Set<AlarmConditionFilterKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {
|
||||||
Map<AlarmSeverity, Set<EntityKey>> sKeys = alarmCreateKeys.get(id);
|
Map<AlarmSeverity, Set<AlarmConditionFilterKey>> sKeys = alarmCreateKeys.get(id);
|
||||||
if (sKeys == null) {
|
if (sKeys == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
Set<EntityKey> keys = sKeys.get(severity);
|
Set<AlarmConditionFilterKey> keys = sKeys.get(severity);
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
@ -129,8 +132,8 @@ class ProfileState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<EntityKey> getClearAlarmKeys(String id) {
|
Set<AlarmConditionFilterKey> getClearAlarmKeys(String id) {
|
||||||
Set<EntityKey> keys = alarmClearKeys.get(id);
|
Set<AlarmConditionFilterKey> keys = alarmClearKeys.get(id);
|
||||||
if (keys == null) {
|
if (keys == null) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
package org.thingsboard.rule.engine.profile;
|
package org.thingsboard.rule.engine.profile;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.query.EntityKey;
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
import org.thingsboard.server.common.data.query.EntityKeyType;
|
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||||
|
|
||||||
@ -24,11 +26,11 @@ import java.util.Set;
|
|||||||
class SnapshotUpdate {
|
class SnapshotUpdate {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final EntityKeyType type;
|
private final AlarmConditionKeyType type;
|
||||||
@Getter
|
@Getter
|
||||||
private final Set<EntityKey> keys;
|
private final Set<AlarmConditionFilterKey> keys;
|
||||||
|
|
||||||
SnapshotUpdate(EntityKeyType type, Set<EntityKey> keys) {
|
SnapshotUpdate(AlarmConditionKeyType type, Set<AlarmConditionFilterKey> keys) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
|
|||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
|
||||||
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
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.DeviceProfileData;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||||
@ -44,6 +47,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
|
|||||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||||
|
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
||||||
import org.thingsboard.server.common.data.query.DynamicValue;
|
import org.thingsboard.server.common.data.query.DynamicValue;
|
||||||
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
|
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
|
||||||
import org.thingsboard.server.common.data.query.EntityKey;
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
@ -62,6 +66,7 @@ import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
|
|||||||
import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
|
import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
|
||||||
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -69,6 +74,7 @@ import java.util.TreeMap;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@ -141,8 +147,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
DeviceProfile deviceProfile = new DeviceProfile();
|
DeviceProfile deviceProfile = new DeviceProfile();
|
||||||
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||||
|
|
||||||
KeyFilter highTempFilter = new KeyFilter();
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
|
||||||
highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
||||||
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
@ -157,8 +163,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
dpa.setAlarmType("highTemperatureAlarm");
|
dpa.setAlarmType("highTemperatureAlarm");
|
||||||
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
|
||||||
|
|
||||||
KeyFilter lowTempFilter = new KeyFilter();
|
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate();
|
NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate();
|
||||||
lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||||
@ -203,6 +209,175 @@ public class TbDeviceProfileNodeTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstantKeyFilterSimple() throws Exception {
|
||||||
|
init();
|
||||||
|
|
||||||
|
DeviceProfile deviceProfile = new DeviceProfile();
|
||||||
|
deviceProfile.setId(deviceProfileId);
|
||||||
|
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||||
|
|
||||||
|
Device device = new Device();
|
||||||
|
device.setId(deviceId);
|
||||||
|
device.setCustomerId(customerId);
|
||||||
|
|
||||||
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey(
|
||||||
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarmEnabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity();
|
||||||
|
attributeKvEntity.setId(compositeKey);
|
||||||
|
attributeKvEntity.setBooleanValue(Boolean.TRUE);
|
||||||
|
attributeKvEntity.setLastUpdateTs(System.currentTimeMillis());
|
||||||
|
|
||||||
|
AttributeKvEntry entry = attributeKvEntity.toData();
|
||||||
|
ListenableFuture<List<AttributeKvEntry>> attrListListenableFuture = Futures.immediateFuture(Collections.singletonList(entry));
|
||||||
|
|
||||||
|
AlarmConditionFilter alarmEnabledFilter = new AlarmConditionFilter();
|
||||||
|
alarmEnabledFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.CONSTANT, "alarmEnabled"));
|
||||||
|
alarmEnabledFilter.setValue(Boolean.TRUE);
|
||||||
|
alarmEnabledFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
|
BooleanFilterPredicate alarmEnabledPredicate = new BooleanFilterPredicate();
|
||||||
|
alarmEnabledPredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
|
alarmEnabledPredicate.setValue(new FilterPredicateValue<>(
|
||||||
|
Boolean.FALSE,
|
||||||
|
null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarmEnabled")
|
||||||
|
));
|
||||||
|
alarmEnabledFilter.setPredicate(alarmEnabledPredicate);
|
||||||
|
|
||||||
|
AlarmConditionFilter temperatureFilter = new AlarmConditionFilter();
|
||||||
|
temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
|
temperatureFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate();
|
||||||
|
temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
|
temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null));
|
||||||
|
temperatureFilter.setPredicate(temperaturePredicate);
|
||||||
|
|
||||||
|
AlarmCondition alarmCondition = new AlarmCondition();
|
||||||
|
alarmCondition.setCondition(Arrays.asList(alarmEnabledFilter, temperatureFilter));
|
||||||
|
AlarmRule alarmRule = new AlarmRule();
|
||||||
|
alarmRule.setCondition(alarmCondition);
|
||||||
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm();
|
||||||
|
dpa.setId("alarmEnabledAlarmID");
|
||||||
|
dpa.setAlarmType("alarmEnabledAlarm");
|
||||||
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
|
||||||
|
|
||||||
|
deviceProfileData.setAlarms(Collections.singletonList(dpa));
|
||||||
|
deviceProfile.setProfileData(deviceProfileData);
|
||||||
|
|
||||||
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile);
|
||||||
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature")))
|
||||||
|
.thenReturn(Futures.immediateFuture(Collections.emptyList()));
|
||||||
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm"))
|
||||||
|
.thenReturn(Futures.immediateFuture(null));
|
||||||
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg());
|
||||||
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
|
||||||
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
|
||||||
|
.thenReturn(attrListListenableFuture);
|
||||||
|
|
||||||
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
|
||||||
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString()))
|
||||||
|
.thenReturn(theMsg);
|
||||||
|
|
||||||
|
ObjectNode data = mapper.createObjectNode();
|
||||||
|
data.put("temperature", 21);
|
||||||
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(),
|
||||||
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null);
|
||||||
|
|
||||||
|
node.onMsg(ctx, msg);
|
||||||
|
verify(ctx).tellSuccess(msg);
|
||||||
|
verify(ctx).tellNext(theMsg, "Alarm Created");
|
||||||
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstantKeyFilterInherited() throws Exception {
|
||||||
|
init();
|
||||||
|
|
||||||
|
DeviceProfile deviceProfile = new DeviceProfile();
|
||||||
|
deviceProfile.setId(deviceProfileId);
|
||||||
|
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||||
|
|
||||||
|
Device device = new Device();
|
||||||
|
device.setId(deviceId);
|
||||||
|
device.setCustomerId(customerId);
|
||||||
|
|
||||||
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey(
|
||||||
|
EntityType.TENANT, tenantId.getId(), "SERVER_SCOPE", "alarmEnabled"
|
||||||
|
);
|
||||||
|
|
||||||
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity();
|
||||||
|
attributeKvEntity.setId(compositeKey);
|
||||||
|
attributeKvEntity.setBooleanValue(Boolean.TRUE);
|
||||||
|
attributeKvEntity.setLastUpdateTs(System.currentTimeMillis());
|
||||||
|
|
||||||
|
AttributeKvEntry entry = attributeKvEntity.toData();
|
||||||
|
ListenableFuture<Optional<AttributeKvEntry>> attrListListenableFuture = Futures.immediateFuture(Optional.of(entry));
|
||||||
|
|
||||||
|
AlarmConditionFilter alarmEnabledFilter = new AlarmConditionFilter();
|
||||||
|
alarmEnabledFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.CONSTANT, "alarmEnabled"));
|
||||||
|
alarmEnabledFilter.setValue(Boolean.TRUE);
|
||||||
|
alarmEnabledFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
|
BooleanFilterPredicate alarmEnabledPredicate = new BooleanFilterPredicate();
|
||||||
|
alarmEnabledPredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
|
alarmEnabledPredicate.setValue(new FilterPredicateValue<>(
|
||||||
|
Boolean.FALSE,
|
||||||
|
null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarmEnabled", true)
|
||||||
|
));
|
||||||
|
alarmEnabledFilter.setPredicate(alarmEnabledPredicate);
|
||||||
|
|
||||||
|
AlarmConditionFilter temperatureFilter = new AlarmConditionFilter();
|
||||||
|
temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
|
temperatureFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate();
|
||||||
|
temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
|
temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null));
|
||||||
|
temperatureFilter.setPredicate(temperaturePredicate);
|
||||||
|
|
||||||
|
AlarmCondition alarmCondition = new AlarmCondition();
|
||||||
|
alarmCondition.setCondition(Arrays.asList(alarmEnabledFilter, temperatureFilter));
|
||||||
|
AlarmRule alarmRule = new AlarmRule();
|
||||||
|
alarmRule.setCondition(alarmCondition);
|
||||||
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm();
|
||||||
|
dpa.setId("alarmEnabledAlarmID");
|
||||||
|
dpa.setAlarmType("alarmEnabledAlarm");
|
||||||
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
|
||||||
|
|
||||||
|
deviceProfileData.setAlarms(Collections.singletonList(dpa));
|
||||||
|
deviceProfile.setProfileData(deviceProfileData);
|
||||||
|
|
||||||
|
Mockito.when(deviceService.findDeviceById(tenantId, deviceId)).thenReturn(device);
|
||||||
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile);
|
||||||
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature")))
|
||||||
|
.thenReturn(Futures.immediateFuture(Collections.emptyList()));
|
||||||
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm"))
|
||||||
|
.thenReturn(Futures.immediateFuture(null));
|
||||||
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg());
|
||||||
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
|
||||||
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
|
||||||
|
.thenReturn(Futures.immediateFuture(Collections.emptyList()));
|
||||||
|
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString()))
|
||||||
|
.thenReturn(Futures.immediateFuture(Optional.empty()));
|
||||||
|
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString()))
|
||||||
|
.thenReturn(attrListListenableFuture);
|
||||||
|
|
||||||
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
|
||||||
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString()))
|
||||||
|
.thenReturn(theMsg);
|
||||||
|
|
||||||
|
ObjectNode data = mapper.createObjectNode();
|
||||||
|
data.put("temperature", 21);
|
||||||
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(),
|
||||||
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null);
|
||||||
|
|
||||||
|
node.onMsg(ctx, msg);
|
||||||
|
verify(ctx).tellSuccess(msg);
|
||||||
|
verify(ctx).tellNext(theMsg, "Alarm Created");
|
||||||
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCurrentDeviceAttributeForDynamicValue() throws Exception {
|
public void testCurrentDeviceAttributeForDynamicValue() throws Exception {
|
||||||
init();
|
init();
|
||||||
@ -228,8 +403,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
||||||
Futures.immediateFuture(Collections.singletonList(entry));
|
Futures.immediateFuture(Collections.singletonList(entry));
|
||||||
|
|
||||||
KeyFilter highTempFilter = new KeyFilter();
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
|
||||||
highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
||||||
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
@ -303,8 +478,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||||
Futures.immediateFuture(Optional.of(entry));
|
Futures.immediateFuture(Optional.of(entry));
|
||||||
|
|
||||||
KeyFilter lowTempFilter = new KeyFilter();
|
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||||
@ -382,8 +557,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||||
Futures.immediateFuture(Optional.of(entry));
|
Futures.immediateFuture(Optional.of(entry));
|
||||||
|
|
||||||
KeyFilter lowTempFilter = new KeyFilter();
|
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||||
@ -454,8 +629,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
||||||
Futures.immediateFuture(Collections.singletonList(entry));
|
Futures.immediateFuture(Collections.singletonList(entry));
|
||||||
|
|
||||||
KeyFilter lowTempFilter = new KeyFilter();
|
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
@ -502,9 +677,9 @@ public class TbDeviceProfileNodeTest {
|
|||||||
verify(ctx).tellSuccess(msg);
|
verify(ctx).tellSuccess(msg);
|
||||||
verify(ctx).tellNext(theMsg, "Alarm Created");
|
verify(ctx).tellNext(theMsg, "Alarm Created");
|
||||||
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomerInheritModeForDynamicValues() throws Exception {
|
public void testCustomerInheritModeForDynamicValues() throws Exception {
|
||||||
init();
|
init();
|
||||||
@ -527,8 +702,8 @@ public class TbDeviceProfileNodeTest {
|
|||||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||||
Futures.immediateFuture(Optional.of(entry));
|
Futures.immediateFuture(Optional.of(entry));
|
||||||
|
|
||||||
KeyFilter lowTempFilter = new KeyFilter();
|
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user