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.alarm.AlarmSeverity;
|
||||
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.DefaultDeviceProfileConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||
@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
AlarmCondition temperatureCondition = new AlarmCondition();
|
||||
temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||
|
||||
KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter();
|
||||
temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
|
||||
AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter();
|
||||
temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
|
||||
temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||
BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
||||
temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||
temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||
temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
|
||||
|
||||
KeyFilter temperatureTimeseriesFilter = new KeyFilter();
|
||||
temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter();
|
||||
temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||
temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
AlarmCondition clearTemperatureCondition = new AlarmCondition();
|
||||
clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||
|
||||
KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();
|
||||
clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter();
|
||||
clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||
clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
|
||||
@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
AlarmCondition humidityCondition = new AlarmCondition();
|
||||
humidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||
|
||||
KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter();
|
||||
humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag"));
|
||||
AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter();
|
||||
humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag"));
|
||||
humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||
BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
|
||||
humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||
humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||
humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
|
||||
|
||||
KeyFilter humidityTimeseriesFilter = new KeyFilter();
|
||||
humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
||||
AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter();
|
||||
humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
|
||||
humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||
humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||
@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
||||
AlarmCondition clearHumidityCondition = new AlarmCondition();
|
||||
clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||
|
||||
KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();
|
||||
clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
||||
AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter();
|
||||
clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
|
||||
clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||
clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
|
||||
|
||||
@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class AlarmCondition {
|
||||
|
||||
private List<KeyFilter> condition;
|
||||
private List<AlarmConditionFilter> condition;
|
||||
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;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
|
||||
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.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.AlarmRule;
|
||||
import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
|
||||
@ -45,6 +49,7 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
class AlarmRuleState {
|
||||
|
||||
private final AlarmSeverity severity;
|
||||
@ -52,12 +57,12 @@ class AlarmRuleState {
|
||||
private final AlarmConditionSpec spec;
|
||||
private final long requiredDurationInMs;
|
||||
private final long requiredRepeats;
|
||||
private final Set<EntityKey> entityKeys;
|
||||
private final Set<AlarmConditionFilterKey> entityKeys;
|
||||
private PersistedAlarmRuleState state;
|
||||
private boolean updateFlag;
|
||||
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.alarmRule = alarmRule;
|
||||
this.entityKeys = entityKeys;
|
||||
@ -84,8 +89,8 @@ class AlarmRuleState {
|
||||
this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;
|
||||
}
|
||||
|
||||
public boolean validateTsUpdate(Set<EntityKey> changedKeys) {
|
||||
for (EntityKey key : changedKeys) {
|
||||
public boolean validateTsUpdate(Set<AlarmConditionFilterKey> changedKeys) {
|
||||
for (AlarmConditionFilterKey key : changedKeys) {
|
||||
if (entityKeys.contains(key)) {
|
||||
return true;
|
||||
}
|
||||
@ -93,18 +98,14 @@ class AlarmRuleState {
|
||||
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.
|
||||
for (EntityKey key : entityKeys) {
|
||||
if (key.getType().equals(EntityKeyType.TIME_SERIES)) {
|
||||
for (AlarmConditionFilterKey key : entityKeys) {
|
||||
if (key.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (EntityKey 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());
|
||||
}
|
||||
for (AlarmConditionFilterKey key : changedKeys) {
|
||||
if (entityKeys.contains(key)) {
|
||||
return true;
|
||||
}
|
||||
@ -259,16 +260,46 @@ class AlarmRuleState {
|
||||
|
||||
private boolean eval(AlarmCondition condition, DataSnapshot data) {
|
||||
boolean eval = true;
|
||||
for (KeyFilter keyFilter : condition.getCondition()) {
|
||||
EntityKeyValue value = data.getValue(keyFilter.getKey());
|
||||
for (var filter : condition.getCondition()) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
eval = eval && eval(data, value, keyFilter.getPredicate());
|
||||
eval = eval && eval(data, value, filter.getPredicate());
|
||||
}
|
||||
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) {
|
||||
switch (predicate.getType()) {
|
||||
case STRING:
|
||||
@ -389,22 +420,13 @@ class AlarmRuleState {
|
||||
if (value.getDynamicValue() != null) {
|
||||
switch (value.getDynamicValue().getSourceType()) {
|
||||
case CURRENT_DEVICE:
|
||||
ekv = data.getValue(new EntityKey(EntityKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
||||
if (ekv == null) {
|
||||
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()) {
|
||||
ekv = data.getValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
|
||||
if (ekv != null || !value.getDynamicValue().isInherit()) {
|
||||
break;
|
||||
}
|
||||
case CURRENT_CUSTOMER:
|
||||
ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
|
||||
if(ekv != null || !value.getDynamicValue().isInherit()) {
|
||||
if (ekv != null || !value.getDynamicValue().isInherit()) {
|
||||
break;
|
||||
}
|
||||
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.AlarmSeverity;
|
||||
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.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
@ -138,9 +139,9 @@ class AlarmState {
|
||||
public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
|
||||
if (update != null) {
|
||||
//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());
|
||||
} else if (update.getType().equals(EntityKeyType.ATTRIBUTE)) {
|
||||
} else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
|
||||
return state.validateAttrUpdate(update.getKeys());
|
||||
}
|
||||
}
|
||||
@ -252,7 +253,7 @@ class AlarmState {
|
||||
String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
|
||||
|
||||
if (StringUtils.isNotEmpty(alarmDetailsStr)) {
|
||||
for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
|
||||
for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
|
||||
EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
|
||||
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.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.EntityKeyType;
|
||||
|
||||
@ -30,55 +32,42 @@ class DataSnapshot {
|
||||
@Getter
|
||||
@Setter
|
||||
private long ts;
|
||||
private final Set<EntityKey> keys;
|
||||
private final Map<EntityKey, EntityKeyValue> values = new ConcurrentHashMap<>();
|
||||
private final Set<AlarmConditionFilterKey> keys;
|
||||
private final Map<AlarmConditionFilterKey, EntityKeyValue> values = new ConcurrentHashMap<>();
|
||||
|
||||
DataSnapshot(Set<EntityKey> entityKeysToFetch) {
|
||||
DataSnapshot(Set<AlarmConditionFilterKey> 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) {
|
||||
switch (key.getType()) {
|
||||
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);
|
||||
}
|
||||
values.remove(toConditionKey(key));
|
||||
}
|
||||
|
||||
boolean putValue(EntityKey key, long newTs, EntityKeyValue value) {
|
||||
boolean updateOfTs = 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;
|
||||
boolean putValue(AlarmConditionFilterKey key, long newTs, EntityKeyValue value) {
|
||||
return putIfKeyExists(key, value, ts != newTs);
|
||||
}
|
||||
|
||||
private boolean putIfKeyExists(EntityKey key, EntityKeyValue value, boolean updateOfTs) {
|
||||
private boolean putIfKeyExists(AlarmConditionFilterKey key, EntityKeyValue value, boolean updateOfTs) {
|
||||
if (keys.contains(key)) {
|
||||
EntityKeyValue oldValue = values.put(key, value);
|
||||
if (updateOfTs) {
|
||||
@ -91,25 +80,7 @@ class DataSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
EntityKeyValue getValue(EntityKey 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 {
|
||||
EntityKeyValue getValue(AlarmConditionFilterKey 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.DeviceProfile;
|
||||
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.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||
@ -97,10 +99,10 @@ class DeviceState {
|
||||
}
|
||||
|
||||
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);
|
||||
if (latestValues != null) {
|
||||
Set<EntityKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
|
||||
Set<AlarmConditionFilterKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
|
||||
keysToFetch.removeAll(oldKeys);
|
||||
if (!keysToFetch.isEmpty()) {
|
||||
addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
|
||||
@ -260,29 +262,29 @@ class DeviceState {
|
||||
}
|
||||
|
||||
private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) {
|
||||
Set<EntityKey> keys = new HashSet<>();
|
||||
Set<AlarmConditionFilterKey> keys = new HashSet<>();
|
||||
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))) {
|
||||
keys.add(entityKey);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
long newTs = 0;
|
||||
Set<EntityKey> keys = new HashSet<>();
|
||||
Set<AlarmConditionFilterKey> keys = new HashSet<>();
|
||||
for (AttributeKvEntry entry : attributes) {
|
||||
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))) {
|
||||
keys.add(entityKey);
|
||||
}
|
||||
}
|
||||
latestValues.setTs(newTs);
|
||||
return new SnapshotUpdate(EntityKeyType.ATTRIBUTE, keys);
|
||||
return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys);
|
||||
}
|
||||
|
||||
private static EntityKeyType getKeyTypeFromScope(String scope) {
|
||||
@ -298,37 +300,22 @@ class DeviceState {
|
||||
}
|
||||
|
||||
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);
|
||||
addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<EntityKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {
|
||||
Set<String> serverAttributeKeys = new HashSet<>();
|
||||
Set<String> clientAttributeKeys = new HashSet<>();
|
||||
Set<String> sharedAttributeKeys = new HashSet<>();
|
||||
Set<String> commonAttributeKeys = new HashSet<>();
|
||||
private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<AlarmConditionFilterKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {
|
||||
Set<String> attributeKeys = new HashSet<>();
|
||||
Set<String> latestTsKeys = new HashSet<>();
|
||||
|
||||
Device device = null;
|
||||
for (EntityKey entityKey : entityKeysToFetch) {
|
||||
for (AlarmConditionFilterKey entityKey : entityKeysToFetch) {
|
||||
String key = entityKey.getKey();
|
||||
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:
|
||||
serverAttributeKeys.add(key);
|
||||
clientAttributeKeys.add(key);
|
||||
sharedAttributeKeys.add(key);
|
||||
commonAttributeKeys.add(key);
|
||||
attributeKeys.add(key);
|
||||
break;
|
||||
case TIME_SERIES:
|
||||
latestTsKeys.add(key);
|
||||
@ -361,32 +348,22 @@ class DeviceState {
|
||||
List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get();
|
||||
for (TsKvEntry entry : data) {
|
||||
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()) {
|
||||
addToSnapshot(result, commonAttributeKeys,
|
||||
ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, clientAttributeKeys).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());
|
||||
if (!attributeKeys.isEmpty()) {
|
||||
addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, attributeKeys).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());
|
||||
}
|
||||
}
|
||||
|
||||
private void addToSnapshot(DataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) {
|
||||
private void addToSnapshot(DataSnapshot snapshot, List<AttributeKvEntry> data) {
|
||||
for (AttributeKvEntry entry : data) {
|
||||
if (entry.getValue() != null) {
|
||||
EntityKeyValue value = toEntityValue(entry);
|
||||
snapshot.putValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
||||
if (commonAttributeKeys.contains(entry.getKey())) {
|
||||
snapshot.putValue(new EntityKey(EntityKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
||||
}
|
||||
snapshot.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
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.DeviceProfileAlarm;
|
||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||
@ -50,10 +53,10 @@ class ProfileState {
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>();
|
||||
@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, Set<EntityKey>> alarmClearKeys = new HashMap<>();
|
||||
private final Map<String, Map<AlarmSeverity, Set<AlarmConditionFilterKey>>> alarmCreateKeys = new HashMap<>();
|
||||
private final Map<String, Set<AlarmConditionFilterKey>> alarmClearKeys = new HashMap<>();
|
||||
|
||||
ProfileState(DeviceProfile deviceProfile) {
|
||||
updateDeviceProfile(deviceProfile);
|
||||
@ -68,18 +71,18 @@ class ProfileState {
|
||||
if (deviceProfile.getProfileData().getAlarms() != null) {
|
||||
alarmSettings.addAll(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) -> {
|
||||
Set<EntityKey> ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());
|
||||
for (KeyFilter keyFilter : alarmRule.getCondition().getCondition()) {
|
||||
var ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());
|
||||
for (var keyFilter : alarmRule.getCondition().getCondition()) {
|
||||
entityKeys.add(keyFilter.getKey());
|
||||
ruleKeys.add(keyFilter.getKey());
|
||||
addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
|
||||
}
|
||||
}));
|
||||
if (alarm.getClearRule() != null) {
|
||||
Set<EntityKey> clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
|
||||
for (KeyFilter keyFilter : alarm.getClearRule().getCondition().getCondition()) {
|
||||
var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
|
||||
for (var keyFilter : alarm.getClearRule().getCondition().getCondition()) {
|
||||
entityKeys.add(keyFilter.getKey());
|
||||
clearAlarmKeys.add(keyFilter.getKey());
|
||||
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()) {
|
||||
case STRING:
|
||||
case NUMERIC:
|
||||
@ -98,7 +101,7 @@ class ProfileState {
|
||||
if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
|
||||
value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
|
||||
value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) {
|
||||
EntityKey entityKey = new EntityKey(EntityKeyType.ATTRIBUTE, value.getSourceAttribute());
|
||||
AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getSourceAttribute());
|
||||
entityKeys.add(entityKey);
|
||||
ruleKeys.add(entityKey);
|
||||
}
|
||||
@ -115,12 +118,12 @@ class ProfileState {
|
||||
return deviceProfile.getId();
|
||||
}
|
||||
|
||||
Set<EntityKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {
|
||||
Map<AlarmSeverity, Set<EntityKey>> sKeys = alarmCreateKeys.get(id);
|
||||
Set<AlarmConditionFilterKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {
|
||||
Map<AlarmSeverity, Set<AlarmConditionFilterKey>> sKeys = alarmCreateKeys.get(id);
|
||||
if (sKeys == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
Set<EntityKey> keys = sKeys.get(severity);
|
||||
Set<AlarmConditionFilterKey> keys = sKeys.get(severity);
|
||||
if (keys == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
@ -129,8 +132,8 @@ class ProfileState {
|
||||
}
|
||||
}
|
||||
|
||||
Set<EntityKey> getClearAlarmKeys(String id) {
|
||||
Set<EntityKey> keys = alarmClearKeys.get(id);
|
||||
Set<AlarmConditionFilterKey> getClearAlarmKeys(String id) {
|
||||
Set<AlarmConditionFilterKey> keys = alarmClearKeys.get(id);
|
||||
if (keys == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
package org.thingsboard.rule.engine.profile;
|
||||
|
||||
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.EntityKeyType;
|
||||
|
||||
@ -24,11 +26,11 @@ import java.util.Set;
|
||||
class SnapshotUpdate {
|
||||
|
||||
@Getter
|
||||
private final EntityKeyType type;
|
||||
private final AlarmConditionKeyType type;
|
||||
@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.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.alarm.AlarmSeverity;
|
||||
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.DeviceProfileAlarm;
|
||||
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.TenantId;
|
||||
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.DynamicValueSourceType;
|
||||
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.timeseries.TimeseriesService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -69,6 +74,7 @@ import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ -141,8 +147,8 @@ public class TbDeviceProfileNodeTest {
|
||||
DeviceProfile deviceProfile = new DeviceProfile();
|
||||
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||
|
||||
KeyFilter highTempFilter = new KeyFilter();
|
||||
highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
|
||||
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
||||
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
@ -157,8 +163,8 @@ public class TbDeviceProfileNodeTest {
|
||||
dpa.setAlarmType("highTemperatureAlarm");
|
||||
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
|
||||
|
||||
KeyFilter lowTempFilter = new KeyFilter();
|
||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate();
|
||||
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
|
||||
public void testCurrentDeviceAttributeForDynamicValue() throws Exception {
|
||||
init();
|
||||
@ -228,8 +403,8 @@ public class TbDeviceProfileNodeTest {
|
||||
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
||||
Futures.immediateFuture(Collections.singletonList(entry));
|
||||
|
||||
KeyFilter highTempFilter = new KeyFilter();
|
||||
highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
|
||||
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
|
||||
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
@ -303,8 +478,8 @@ public class TbDeviceProfileNodeTest {
|
||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||
Futures.immediateFuture(Optional.of(entry));
|
||||
|
||||
KeyFilter lowTempFilter = new KeyFilter();
|
||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||
@ -382,8 +557,8 @@ public class TbDeviceProfileNodeTest {
|
||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||
Futures.immediateFuture(Optional.of(entry));
|
||||
|
||||
KeyFilter lowTempFilter = new KeyFilter();
|
||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||
@ -454,8 +629,8 @@ public class TbDeviceProfileNodeTest {
|
||||
ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
|
||||
Futures.immediateFuture(Collections.singletonList(entry));
|
||||
|
||||
KeyFilter lowTempFilter = new KeyFilter();
|
||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
@ -502,9 +677,9 @@ public class TbDeviceProfileNodeTest {
|
||||
verify(ctx).tellSuccess(msg);
|
||||
verify(ctx).tellNext(theMsg, "Alarm Created");
|
||||
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomerInheritModeForDynamicValues() throws Exception {
|
||||
init();
|
||||
@ -527,8 +702,8 @@ public class TbDeviceProfileNodeTest {
|
||||
ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
|
||||
Futures.immediateFuture(Optional.of(entry));
|
||||
|
||||
KeyFilter lowTempFilter = new KeyFilter();
|
||||
lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||
AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
|
||||
lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
|
||||
lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||
NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
|
||||
lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user