Commit 3cd964327a2686f6f2f6db4b8cccdd67b20a10dc

Authored by Andrii Shvaika
Committed by Andrew Shvayka
1 parent 72374979

Constant filters for device profile

@@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile;
36 import org.thingsboard.server.common.data.User; 36 import org.thingsboard.server.common.data.User;
37 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 import org.thingsboard.server.common.data.device.profile.AlarmCondition; 38 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  39 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
  40 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  41 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
39 import org.thingsboard.server.common.data.device.profile.AlarmRule; 42 import org.thingsboard.server.common.data.device.profile.AlarmRule;
40 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; 43 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
41 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; 44 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
@@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
290 AlarmCondition temperatureCondition = new AlarmCondition(); 293 AlarmCondition temperatureCondition = new AlarmCondition();
291 temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); 294 temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
292 295
293 - KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter();  
294 - temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag")); 296 + AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter();
  297 + temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
295 temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); 298 temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
296 BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); 299 BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
297 temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); 300 temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
298 temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); 301 temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
299 temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); 302 temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
300 303
301 - KeyFilter temperatureTimeseriesFilter = new KeyFilter();  
302 - temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 304 + AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter();
  305 + temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
303 temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 306 temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
304 NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); 307 NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
305 temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 308 temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
317 AlarmCondition clearTemperatureCondition = new AlarmCondition(); 320 AlarmCondition clearTemperatureCondition = new AlarmCondition();
318 clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); 321 clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
319 322
320 - KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();  
321 - clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 323 + AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter();
  324 + clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
322 clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 325 clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
323 NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); 326 NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
324 clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); 327 clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
@@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
340 AlarmCondition humidityCondition = new AlarmCondition(); 343 AlarmCondition humidityCondition = new AlarmCondition();
341 humidityCondition.setSpec(new SimpleAlarmConditionSpec()); 344 humidityCondition.setSpec(new SimpleAlarmConditionSpec());
342 345
343 - KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter();  
344 - humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag")); 346 + AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter();
  347 + humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag"));
345 humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); 348 humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
346 BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); 349 BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
347 humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); 350 humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
348 humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); 351 humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
349 humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); 352 humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
350 353
351 - KeyFilter humidityTimeseriesFilter = new KeyFilter();  
352 - humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); 354 + AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter();
  355 + humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
353 humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 356 humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
354 NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); 357 NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
355 humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); 358 humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
@@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
368 AlarmCondition clearHumidityCondition = new AlarmCondition(); 371 AlarmCondition clearHumidityCondition = new AlarmCondition();
369 clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); 372 clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
370 373
371 - KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();  
372 - clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); 374 + AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter();
  375 + clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity"));
373 clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); 376 clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
374 NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); 377 NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
375 clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); 378 clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
@@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit; @@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
26 @JsonIgnoreProperties(ignoreUnknown = true) 26 @JsonIgnoreProperties(ignoreUnknown = true)
27 public class AlarmCondition { 27 public class AlarmCondition {
28 28
29 - private List<KeyFilter> condition; 29 + private List<AlarmConditionFilter> condition;
30 private AlarmConditionSpec spec; 30 private AlarmConditionSpec spec;
31 31
32 } 32 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.query.EntityKeyValueType;
  20 +import org.thingsboard.server.common.data.query.KeyFilterPredicate;
  21 +
  22 +@Data
  23 +public class AlarmConditionFilter {
  24 +
  25 + private AlarmConditionFilterKey key;
  26 + private EntityKeyValueType valueType;
  27 + private Object value;
  28 + private KeyFilterPredicate predicate;
  29 +
  30 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class AlarmConditionFilterKey {
  22 +
  23 + private final AlarmConditionKeyType type;
  24 + private final String key;
  25 +
  26 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +public enum AlarmConditionKeyType {
  19 + ATTRIBUTE,
  20 + TIME_SERIES,
  21 + ENTITY_FIELD,
  22 + CONSTANT
  23 +}
@@ -16,9 +16,13 @@ @@ -16,9 +16,13 @@
16 package org.thingsboard.rule.engine.profile; 16 package org.thingsboard.rule.engine.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState; 20 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
20 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 21 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
21 import org.thingsboard.server.common.data.device.profile.AlarmCondition; 22 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  23 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
  24 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  25 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
22 import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec; 26 import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
23 import org.thingsboard.server.common.data.device.profile.AlarmRule; 27 import org.thingsboard.server.common.data.device.profile.AlarmRule;
24 import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule; 28 import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
@@ -45,6 +49,7 @@ import java.util.Set; @@ -45,6 +49,7 @@ import java.util.Set;
45 import java.util.function.Function; 49 import java.util.function.Function;
46 50
47 @Data 51 @Data
  52 +@Slf4j
48 class AlarmRuleState { 53 class AlarmRuleState {
49 54
50 private final AlarmSeverity severity; 55 private final AlarmSeverity severity;
@@ -52,12 +57,12 @@ class AlarmRuleState { @@ -52,12 +57,12 @@ class AlarmRuleState {
52 private final AlarmConditionSpec spec; 57 private final AlarmConditionSpec spec;
53 private final long requiredDurationInMs; 58 private final long requiredDurationInMs;
54 private final long requiredRepeats; 59 private final long requiredRepeats;
55 - private final Set<EntityKey> entityKeys; 60 + private final Set<AlarmConditionFilterKey> entityKeys;
56 private PersistedAlarmRuleState state; 61 private PersistedAlarmRuleState state;
57 private boolean updateFlag; 62 private boolean updateFlag;
58 private final DynamicPredicateValueCtx dynamicPredicateValueCtx; 63 private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
59 64
60 - AlarmRuleState(AlarmSeverity severity, AlarmRule alarmRule, Set<EntityKey> entityKeys, PersistedAlarmRuleState state, DynamicPredicateValueCtx dynamicPredicateValueCtx) { 65 + AlarmRuleState(AlarmSeverity severity, AlarmRule alarmRule, Set<AlarmConditionFilterKey> entityKeys, PersistedAlarmRuleState state, DynamicPredicateValueCtx dynamicPredicateValueCtx) {
61 this.severity = severity; 66 this.severity = severity;
62 this.alarmRule = alarmRule; 67 this.alarmRule = alarmRule;
63 this.entityKeys = entityKeys; 68 this.entityKeys = entityKeys;
@@ -84,8 +89,8 @@ class AlarmRuleState { @@ -84,8 +89,8 @@ class AlarmRuleState {
84 this.dynamicPredicateValueCtx = dynamicPredicateValueCtx; 89 this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;
85 } 90 }
86 91
87 - public boolean validateTsUpdate(Set<EntityKey> changedKeys) {  
88 - for (EntityKey key : changedKeys) { 92 + public boolean validateTsUpdate(Set<AlarmConditionFilterKey> changedKeys) {
  93 + for (AlarmConditionFilterKey key : changedKeys) {
89 if (entityKeys.contains(key)) { 94 if (entityKeys.contains(key)) {
90 return true; 95 return true;
91 } 96 }
@@ -93,18 +98,14 @@ class AlarmRuleState { @@ -93,18 +98,14 @@ class AlarmRuleState {
93 return false; 98 return false;
94 } 99 }
95 100
96 - public boolean validateAttrUpdate(Set<EntityKey> changedKeys) { 101 + public boolean validateAttrUpdate(Set<AlarmConditionFilterKey> changedKeys) {
97 //If the attribute was updated, but no new telemetry arrived - we ignore this until new telemetry is there. 102 //If the attribute was updated, but no new telemetry arrived - we ignore this until new telemetry is there.
98 - for (EntityKey key : entityKeys) {  
99 - if (key.getType().equals(EntityKeyType.TIME_SERIES)) { 103 + for (AlarmConditionFilterKey key : entityKeys) {
  104 + if (key.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
100 return false; 105 return false;
101 } 106 }
102 } 107 }
103 - for (EntityKey key : changedKeys) {  
104 - EntityKeyType keyType = key.getType();  
105 - if (EntityKeyType.CLIENT_ATTRIBUTE.equals(keyType) || EntityKeyType.SERVER_ATTRIBUTE.equals(keyType) || EntityKeyType.SHARED_ATTRIBUTE.equals(keyType)) {  
106 - key = new EntityKey(EntityKeyType.ATTRIBUTE, key.getKey());  
107 - } 108 + for (AlarmConditionFilterKey key : changedKeys) {
108 if (entityKeys.contains(key)) { 109 if (entityKeys.contains(key)) {
109 return true; 110 return true;
110 } 111 }
@@ -259,16 +260,46 @@ class AlarmRuleState { @@ -259,16 +260,46 @@ class AlarmRuleState {
259 260
260 private boolean eval(AlarmCondition condition, DataSnapshot data) { 261 private boolean eval(AlarmCondition condition, DataSnapshot data) {
261 boolean eval = true; 262 boolean eval = true;
262 - for (KeyFilter keyFilter : condition.getCondition()) {  
263 - EntityKeyValue value = data.getValue(keyFilter.getKey()); 263 + for (var filter : condition.getCondition()) {
  264 + EntityKeyValue value;
  265 + if (filter.getKey().getType().equals(AlarmConditionKeyType.CONSTANT)) {
  266 + try {
  267 + value = getConstantValue(filter);
  268 + } catch (RuntimeException e) {
  269 + log.warn("Failed to parse constant value from filter: {}", filter, e);
  270 + value = null;
  271 + }
  272 + } else {
  273 + value = data.getValue(filter.getKey());
  274 + }
264 if (value == null) { 275 if (value == null) {
265 return false; 276 return false;
266 } 277 }
267 - eval = eval && eval(data, value, keyFilter.getPredicate()); 278 + eval = eval && eval(data, value, filter.getPredicate());
268 } 279 }
269 return eval; 280 return eval;
270 } 281 }
271 282
  283 + private EntityKeyValue getConstantValue(AlarmConditionFilter filter) {
  284 + EntityKeyValue value = new EntityKeyValue();
  285 + String valueStr = filter.getValue().toString();
  286 + switch (filter.getValueType()) {
  287 + case STRING:
  288 + value.setStrValue(valueStr);
  289 + break;
  290 + case DATE_TIME:
  291 + value.setLngValue(Long.valueOf(valueStr));
  292 + break;
  293 + case NUMERIC:
  294 + value.setDblValue(Double.valueOf(valueStr));
  295 + break;
  296 + case BOOLEAN:
  297 + value.setBoolValue(Boolean.valueOf(valueStr));
  298 + break;
  299 + }
  300 + return value;
  301 + }
  302 +
272 private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) { 303 private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) {
273 switch (predicate.getType()) { 304 switch (predicate.getType()) {
274 case STRING: 305 case STRING:
@@ -389,23 +420,14 @@ class AlarmRuleState { @@ -389,23 +420,14 @@ class AlarmRuleState {
389 if (value.getDynamicValue() != null) { 420 if (value.getDynamicValue() != null) {
390 switch (value.getDynamicValue().getSourceType()) { 421 switch (value.getDynamicValue().getSourceType()) {
391 case CURRENT_DEVICE: 422 case CURRENT_DEVICE:
392 - ekv = data.getValue(new EntityKey(EntityKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));  
393 - if (ekv == null) {  
394 - ekv = data.getValue(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));  
395 - if (ekv == null) {  
396 - ekv = data.getValue(new EntityKey(EntityKeyType.SHARED_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));  
397 - if (ekv == null) {  
398 - ekv = data.getValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));  
399 - }  
400 - }  
401 - }  
402 - if(ekv != null || !value.getDynamicValue().isInherit()) { 423 + ekv = data.getValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute()));
  424 + if (ekv != null || !value.getDynamicValue().isInherit()) {
403 break; 425 break;
404 } 426 }
405 case CURRENT_CUSTOMER: 427 case CURRENT_CUSTOMER:
406 ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute()); 428 ekv = dynamicPredicateValueCtx.getCustomerValue(value.getDynamicValue().getSourceAttribute());
407 - if(ekv != null || !value.getDynamicValue().isInherit()) {  
408 - break; 429 + if (ekv != null || !value.getDynamicValue().isInherit()) {
  430 + break;
409 } 431 }
410 case CURRENT_TENANT: 432 case CURRENT_TENANT:
411 ekv = dynamicPredicateValueCtx.getTenantValue(value.getDynamicValue().getSourceAttribute()); 433 ekv = dynamicPredicateValueCtx.getTenantValue(value.getDynamicValue().getSourceAttribute());
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Tenant; @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.alarm.Alarm; 29 import org.thingsboard.server.common.data.alarm.Alarm;
30 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 30 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
31 import org.thingsboard.server.common.data.alarm.AlarmStatus; 31 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  32 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
32 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 33 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
33 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
34 import org.thingsboard.server.common.data.id.EntityId; 35 import org.thingsboard.server.common.data.id.EntityId;
@@ -138,9 +139,9 @@ class AlarmState { @@ -138,9 +139,9 @@ class AlarmState {
138 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) { 139 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
139 if (update != null) { 140 if (update != null) {
140 //Check that the update type and that keys match. 141 //Check that the update type and that keys match.
141 - if (update.getType().equals(EntityKeyType.TIME_SERIES)) { 142 + if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
142 return state.validateTsUpdate(update.getKeys()); 143 return state.validateTsUpdate(update.getKeys());
143 - } else if (update.getType().equals(EntityKeyType.ATTRIBUTE)) { 144 + } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
144 return state.validateAttrUpdate(update.getKeys()); 145 return state.validateAttrUpdate(update.getKeys());
145 } 146 }
146 } 147 }
@@ -252,7 +253,7 @@ class AlarmState { @@ -252,7 +253,7 @@ class AlarmState {
252 String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails(); 253 String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
253 254
254 if (StringUtils.isNotEmpty(alarmDetailsStr)) { 255 if (StringUtils.isNotEmpty(alarmDetailsStr)) {
255 - for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) { 256 + for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
256 EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey()); 257 EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
257 alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue)); 258 alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue));
258 } 259 }
@@ -17,6 +17,8 @@ package org.thingsboard.rule.engine.profile; @@ -17,6 +17,8 @@ package org.thingsboard.rule.engine.profile;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
19 import lombok.Setter; 19 import lombok.Setter;
  20 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  21 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
20 import org.thingsboard.server.common.data.query.EntityKey; 22 import org.thingsboard.server.common.data.query.EntityKey;
21 import org.thingsboard.server.common.data.query.EntityKeyType; 23 import org.thingsboard.server.common.data.query.EntityKeyType;
22 24
@@ -30,55 +32,42 @@ class DataSnapshot { @@ -30,55 +32,42 @@ class DataSnapshot {
30 @Getter 32 @Getter
31 @Setter 33 @Setter
32 private long ts; 34 private long ts;
33 - private final Set<EntityKey> keys;  
34 - private final Map<EntityKey, EntityKeyValue> values = new ConcurrentHashMap<>(); 35 + private final Set<AlarmConditionFilterKey> keys;
  36 + private final Map<AlarmConditionFilterKey, EntityKeyValue> values = new ConcurrentHashMap<>();
35 37
36 - DataSnapshot(Set<EntityKey> entityKeysToFetch) { 38 + DataSnapshot(Set<AlarmConditionFilterKey> entityKeysToFetch) {
37 this.keys = entityKeysToFetch; 39 this.keys = entityKeysToFetch;
38 } 40 }
39 41
40 - void removeValue(EntityKey key) {  
41 - switch (key.getType()) {  
42 - case ATTRIBUTE:  
43 - values.remove(key);  
44 - values.remove(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE));  
45 - values.remove(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE));  
46 - values.remove(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE));  
47 - break;  
48 - case CLIENT_ATTRIBUTE:  
49 - case SHARED_ATTRIBUTE:  
50 - case SERVER_ATTRIBUTE:  
51 - values.remove(key);  
52 - values.remove(getAttrKey(key, EntityKeyType.ATTRIBUTE));  
53 - break;  
54 - default:  
55 - values.remove(key);  
56 - } 42 + static AlarmConditionFilterKey toConditionKey(EntityKey key) {
  43 + return new AlarmConditionFilterKey(toConditionKeyType(key.getType()), key.getKey());
57 } 44 }
58 45
59 - boolean putValue(EntityKey key, long newTs, EntityKeyValue value) {  
60 - boolean updateOfTs = ts != newTs;  
61 - boolean result = false;  
62 - switch (key.getType()) { 46 + static AlarmConditionKeyType toConditionKeyType(EntityKeyType keyType) {
  47 + switch (keyType) {
63 case ATTRIBUTE: 48 case ATTRIBUTE:
64 - result |= putIfKeyExists(key, value, updateOfTs);  
65 - result |= putIfKeyExists(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE), value, updateOfTs);  
66 - result |= putIfKeyExists(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE), value, updateOfTs);  
67 - result |= putIfKeyExists(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE), value, updateOfTs);  
68 - break;  
69 - case CLIENT_ATTRIBUTE:  
70 - case SHARED_ATTRIBUTE:  
71 case SERVER_ATTRIBUTE: 49 case SERVER_ATTRIBUTE:
72 - result |= putIfKeyExists(key, value, updateOfTs);  
73 - result |= putIfKeyExists(getAttrKey(key, EntityKeyType.ATTRIBUTE), value, updateOfTs);  
74 - break; 50 + case SHARED_ATTRIBUTE:
  51 + case CLIENT_ATTRIBUTE:
  52 + return AlarmConditionKeyType.ATTRIBUTE;
  53 + case TIME_SERIES:
  54 + return AlarmConditionKeyType.TIME_SERIES;
  55 + case ENTITY_FIELD:
  56 + return AlarmConditionKeyType.ENTITY_FIELD;
75 default: 57 default:
76 - result |= putIfKeyExists(key, value, updateOfTs); 58 + throw new RuntimeException("Not supported entity key: " + keyType.name());
77 } 59 }
78 - return result;  
79 } 60 }
80 61
81 - private boolean putIfKeyExists(EntityKey key, EntityKeyValue value, boolean updateOfTs) { 62 + void removeValue(EntityKey key) {
  63 + values.remove(toConditionKey(key));
  64 + }
  65 +
  66 + boolean putValue(AlarmConditionFilterKey key, long newTs, EntityKeyValue value) {
  67 + return putIfKeyExists(key, value, ts != newTs);
  68 + }
  69 +
  70 + private boolean putIfKeyExists(AlarmConditionFilterKey key, EntityKeyValue value, boolean updateOfTs) {
82 if (keys.contains(key)) { 71 if (keys.contains(key)) {
83 EntityKeyValue oldValue = values.put(key, value); 72 EntityKeyValue oldValue = values.put(key, value);
84 if (updateOfTs) { 73 if (updateOfTs) {
@@ -91,25 +80,7 @@ class DataSnapshot { @@ -91,25 +80,7 @@ class DataSnapshot {
91 } 80 }
92 } 81 }
93 82
94 - EntityKeyValue getValue(EntityKey key) {  
95 - if (EntityKeyType.ATTRIBUTE.equals(key.getType())) {  
96 - EntityKeyValue value = values.get(key);  
97 - if (value == null) {  
98 - value = values.get(getAttrKey(key, EntityKeyType.CLIENT_ATTRIBUTE));  
99 - if (value == null) {  
100 - value = values.get(getAttrKey(key, EntityKeyType.SHARED_ATTRIBUTE));  
101 - if (value == null) {  
102 - value = values.get(getAttrKey(key, EntityKeyType.SERVER_ATTRIBUTE));  
103 - }  
104 - }  
105 - }  
106 - return value;  
107 - } else {  
108 - return values.get(key);  
109 - }  
110 - }  
111 -  
112 - private EntityKey getAttrKey(EntityKey key, EntityKeyType clientAttribute) {  
113 - return new EntityKey(clientAttribute, key.getKey()); 83 + EntityKeyValue getValue(AlarmConditionFilterKey key) {
  84 + return values.get(key);
114 } 85 }
115 } 86 }
@@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.DataConstants; @@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.DataConstants;
26 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
27 import org.thingsboard.server.common.data.DeviceProfile; 27 import org.thingsboard.server.common.data.DeviceProfile;
28 import org.thingsboard.server.common.data.alarm.Alarm; 28 import org.thingsboard.server.common.data.alarm.Alarm;
  29 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  30 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
29 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 31 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
30 import org.thingsboard.server.common.data.id.DeviceId; 32 import org.thingsboard.server.common.data.id.DeviceId;
31 import org.thingsboard.server.common.data.id.DeviceProfileId; 33 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -97,10 +99,10 @@ class DeviceState { @@ -97,10 +99,10 @@ class DeviceState {
97 } 99 }
98 100
99 public void updateProfile(TbContext ctx, DeviceProfile deviceProfile) throws ExecutionException, InterruptedException { 101 public void updateProfile(TbContext ctx, DeviceProfile deviceProfile) throws ExecutionException, InterruptedException {
100 - Set<EntityKey> oldKeys = this.deviceProfile.getEntityKeys(); 102 + Set<AlarmConditionFilterKey> oldKeys = this.deviceProfile.getEntityKeys();
101 this.deviceProfile.updateDeviceProfile(deviceProfile); 103 this.deviceProfile.updateDeviceProfile(deviceProfile);
102 if (latestValues != null) { 104 if (latestValues != null) {
103 - Set<EntityKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys()); 105 + Set<AlarmConditionFilterKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
104 keysToFetch.removeAll(oldKeys); 106 keysToFetch.removeAll(oldKeys);
105 if (!keysToFetch.isEmpty()) { 107 if (!keysToFetch.isEmpty()) {
106 addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues); 108 addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
@@ -260,29 +262,29 @@ class DeviceState { @@ -260,29 +262,29 @@ class DeviceState {
260 } 262 }
261 263
262 private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) { 264 private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) {
263 - Set<EntityKey> keys = new HashSet<>(); 265 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
264 for (KvEntry entry : data) { 266 for (KvEntry entry : data) {
265 - EntityKey entityKey = new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey()); 267 + AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey());
266 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) { 268 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
267 keys.add(entityKey); 269 keys.add(entityKey);
268 } 270 }
269 } 271 }
270 latestValues.setTs(newTs); 272 latestValues.setTs(newTs);
271 - return new SnapshotUpdate(EntityKeyType.TIME_SERIES, keys); 273 + return new SnapshotUpdate(AlarmConditionKeyType.TIME_SERIES, keys);
272 } 274 }
273 275
274 private SnapshotUpdate merge(DataSnapshot latestValues, Set<AttributeKvEntry> attributes, String scope) { 276 private SnapshotUpdate merge(DataSnapshot latestValues, Set<AttributeKvEntry> attributes, String scope) {
275 long newTs = 0; 277 long newTs = 0;
276 - Set<EntityKey> keys = new HashSet<>(); 278 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
277 for (AttributeKvEntry entry : attributes) { 279 for (AttributeKvEntry entry : attributes) {
278 newTs = Math.max(newTs, entry.getLastUpdateTs()); 280 newTs = Math.max(newTs, entry.getLastUpdateTs());
279 - EntityKey entityKey = new EntityKey(getKeyTypeFromScope(scope), entry.getKey()); 281 + AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey());
280 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) { 282 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
281 keys.add(entityKey); 283 keys.add(entityKey);
282 } 284 }
283 } 285 }
284 latestValues.setTs(newTs); 286 latestValues.setTs(newTs);
285 - return new SnapshotUpdate(EntityKeyType.ATTRIBUTE, keys); 287 + return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys);
286 } 288 }
287 289
288 private static EntityKeyType getKeyTypeFromScope(String scope) { 290 private static EntityKeyType getKeyTypeFromScope(String scope) {
@@ -298,37 +300,22 @@ class DeviceState { @@ -298,37 +300,22 @@ class DeviceState {
298 } 300 }
299 301
300 private DataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException { 302 private DataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException {
301 - Set<EntityKey> entityKeysToFetch = deviceProfile.getEntityKeys(); 303 + Set<AlarmConditionFilterKey> entityKeysToFetch = deviceProfile.getEntityKeys();
302 DataSnapshot result = new DataSnapshot(entityKeysToFetch); 304 DataSnapshot result = new DataSnapshot(entityKeysToFetch);
303 addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result); 305 addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
304 return result; 306 return result;
305 } 307 }
306 308
307 - private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<EntityKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {  
308 - Set<String> serverAttributeKeys = new HashSet<>();  
309 - Set<String> clientAttributeKeys = new HashSet<>();  
310 - Set<String> sharedAttributeKeys = new HashSet<>();  
311 - Set<String> commonAttributeKeys = new HashSet<>(); 309 + private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<AlarmConditionFilterKey> entityKeysToFetch, DataSnapshot result) throws InterruptedException, ExecutionException {
  310 + Set<String> attributeKeys = new HashSet<>();
312 Set<String> latestTsKeys = new HashSet<>(); 311 Set<String> latestTsKeys = new HashSet<>();
313 312
314 Device device = null; 313 Device device = null;
315 - for (EntityKey entityKey : entityKeysToFetch) { 314 + for (AlarmConditionFilterKey entityKey : entityKeysToFetch) {
316 String key = entityKey.getKey(); 315 String key = entityKey.getKey();
317 switch (entityKey.getType()) { 316 switch (entityKey.getType()) {
318 - case SERVER_ATTRIBUTE:  
319 - serverAttributeKeys.add(key);  
320 - break;  
321 - case CLIENT_ATTRIBUTE:  
322 - clientAttributeKeys.add(key);  
323 - break;  
324 - case SHARED_ATTRIBUTE:  
325 - sharedAttributeKeys.add(key);  
326 - break;  
327 case ATTRIBUTE: 317 case ATTRIBUTE:
328 - serverAttributeKeys.add(key);  
329 - clientAttributeKeys.add(key);  
330 - sharedAttributeKeys.add(key);  
331 - commonAttributeKeys.add(key); 318 + attributeKeys.add(key);
332 break; 319 break;
333 case TIME_SERIES: 320 case TIME_SERIES:
334 latestTsKeys.add(key); 321 latestTsKeys.add(key);
@@ -361,32 +348,22 @@ class DeviceState { @@ -361,32 +348,22 @@ class DeviceState {
361 List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get(); 348 List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get();
362 for (TsKvEntry entry : data) { 349 for (TsKvEntry entry : data) {
363 if (entry.getValue() != null) { 350 if (entry.getValue() != null) {
364 - result.putValue(new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey()), entry.getTs(), toEntityValue(entry)); 351 + result.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey()), entry.getTs(), toEntityValue(entry));
365 } 352 }
366 } 353 }
367 } 354 }
368 - if (!clientAttributeKeys.isEmpty()) {  
369 - addToSnapshot(result, commonAttributeKeys,  
370 - ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, clientAttributeKeys).get());  
371 - }  
372 - if (!sharedAttributeKeys.isEmpty()) {  
373 - addToSnapshot(result, commonAttributeKeys,  
374 - ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SHARED_SCOPE, sharedAttributeKeys).get());  
375 - }  
376 - if (!serverAttributeKeys.isEmpty()) {  
377 - addToSnapshot(result, commonAttributeKeys,  
378 - ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, serverAttributeKeys).get()); 355 + if (!attributeKeys.isEmpty()) {
  356 + addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.CLIENT_SCOPE, attributeKeys).get());
  357 + addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SHARED_SCOPE, attributeKeys).get());
  358 + addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, attributeKeys).get());
379 } 359 }
380 } 360 }
381 361
382 - private void addToSnapshot(DataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) { 362 + private void addToSnapshot(DataSnapshot snapshot, List<AttributeKvEntry> data) {
383 for (AttributeKvEntry entry : data) { 363 for (AttributeKvEntry entry : data) {
384 if (entry.getValue() != null) { 364 if (entry.getValue() != null) {
385 EntityKeyValue value = toEntityValue(entry); 365 EntityKeyValue value = toEntityValue(entry);
386 - snapshot.putValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);  
387 - if (commonAttributeKeys.contains(entry.getKey())) {  
388 - snapshot.putValue(new EntityKey(EntityKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);  
389 - } 366 + snapshot.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value);
390 } 367 }
391 } 368 }
392 } 369 }
@@ -19,6 +19,9 @@ import lombok.AccessLevel; @@ -19,6 +19,9 @@ import lombok.AccessLevel;
19 import lombok.Getter; 19 import lombok.Getter;
20 import org.thingsboard.server.common.data.DeviceProfile; 20 import org.thingsboard.server.common.data.DeviceProfile;
21 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 21 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  22 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
  23 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  24 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
22 import org.thingsboard.server.common.data.device.profile.AlarmRule; 25 import org.thingsboard.server.common.data.device.profile.AlarmRule;
23 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 26 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
24 import org.thingsboard.server.common.data.id.DeviceProfileId; 27 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -50,10 +53,10 @@ class ProfileState { @@ -50,10 +53,10 @@ class ProfileState {
50 @Getter(AccessLevel.PACKAGE) 53 @Getter(AccessLevel.PACKAGE)
51 private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>(); 54 private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>();
52 @Getter(AccessLevel.PACKAGE) 55 @Getter(AccessLevel.PACKAGE)
53 - private final Set<EntityKey> entityKeys = ConcurrentHashMap.newKeySet(); 56 + private final Set<AlarmConditionFilterKey> entityKeys = ConcurrentHashMap.newKeySet();
54 57
55 - private final Map<String, Map<AlarmSeverity, Set<EntityKey>>> alarmCreateKeys = new HashMap<>();  
56 - private final Map<String, Set<EntityKey>> alarmClearKeys = new HashMap<>(); 58 + private final Map<String, Map<AlarmSeverity, Set<AlarmConditionFilterKey>>> alarmCreateKeys = new HashMap<>();
  59 + private final Map<String, Set<AlarmConditionFilterKey>> alarmClearKeys = new HashMap<>();
57 60
58 ProfileState(DeviceProfile deviceProfile) { 61 ProfileState(DeviceProfile deviceProfile) {
59 updateDeviceProfile(deviceProfile); 62 updateDeviceProfile(deviceProfile);
@@ -68,18 +71,18 @@ class ProfileState { @@ -68,18 +71,18 @@ class ProfileState {
68 if (deviceProfile.getProfileData().getAlarms() != null) { 71 if (deviceProfile.getProfileData().getAlarms() != null) {
69 alarmSettings.addAll(deviceProfile.getProfileData().getAlarms()); 72 alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
70 for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) { 73 for (DeviceProfileAlarm alarm : deviceProfile.getProfileData().getAlarms()) {
71 - Map<AlarmSeverity, Set<EntityKey>> createAlarmKeys = alarmCreateKeys.computeIfAbsent(alarm.getId(), id -> new HashMap<>()); 74 + Map<AlarmSeverity, Set<AlarmConditionFilterKey>> createAlarmKeys = alarmCreateKeys.computeIfAbsent(alarm.getId(), id -> new HashMap<>());
72 alarm.getCreateRules().forEach(((severity, alarmRule) -> { 75 alarm.getCreateRules().forEach(((severity, alarmRule) -> {
73 - Set<EntityKey> ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());  
74 - for (KeyFilter keyFilter : alarmRule.getCondition().getCondition()) { 76 + var ruleKeys = createAlarmKeys.computeIfAbsent(severity, id -> new HashSet<>());
  77 + for (var keyFilter : alarmRule.getCondition().getCondition()) {
75 entityKeys.add(keyFilter.getKey()); 78 entityKeys.add(keyFilter.getKey());
76 ruleKeys.add(keyFilter.getKey()); 79 ruleKeys.add(keyFilter.getKey());
77 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys); 80 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
78 } 81 }
79 })); 82 }));
80 if (alarm.getClearRule() != null) { 83 if (alarm.getClearRule() != null) {
81 - Set<EntityKey> clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());  
82 - for (KeyFilter keyFilter : alarm.getClearRule().getCondition().getCondition()) { 84 + var clearAlarmKeys = alarmClearKeys.computeIfAbsent(alarm.getId(), id -> new HashSet<>());
  85 + for (var keyFilter : alarm.getClearRule().getCondition().getCondition()) {
83 entityKeys.add(keyFilter.getKey()); 86 entityKeys.add(keyFilter.getKey());
84 clearAlarmKeys.add(keyFilter.getKey()); 87 clearAlarmKeys.add(keyFilter.getKey());
85 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys); 88 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys);
@@ -89,7 +92,7 @@ class ProfileState { @@ -89,7 +92,7 @@ class ProfileState {
89 } 92 }
90 } 93 }
91 94
92 - private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<EntityKey> entityKeys, Set<EntityKey> ruleKeys) { 95 + private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<AlarmConditionFilterKey> entityKeys, Set<AlarmConditionFilterKey> ruleKeys) {
93 switch (predicate.getType()) { 96 switch (predicate.getType()) {
94 case STRING: 97 case STRING:
95 case NUMERIC: 98 case NUMERIC:
@@ -98,7 +101,7 @@ class ProfileState { @@ -98,7 +101,7 @@ class ProfileState {
98 if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT || 101 if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
99 value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER || 102 value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
100 value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) { 103 value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) {
101 - EntityKey entityKey = new EntityKey(EntityKeyType.ATTRIBUTE, value.getSourceAttribute()); 104 + AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getSourceAttribute());
102 entityKeys.add(entityKey); 105 entityKeys.add(entityKey);
103 ruleKeys.add(entityKey); 106 ruleKeys.add(entityKey);
104 } 107 }
@@ -115,12 +118,12 @@ class ProfileState { @@ -115,12 +118,12 @@ class ProfileState {
115 return deviceProfile.getId(); 118 return deviceProfile.getId();
116 } 119 }
117 120
118 - Set<EntityKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {  
119 - Map<AlarmSeverity, Set<EntityKey>> sKeys = alarmCreateKeys.get(id); 121 + Set<AlarmConditionFilterKey> getCreateAlarmKeys(String id, AlarmSeverity severity) {
  122 + Map<AlarmSeverity, Set<AlarmConditionFilterKey>> sKeys = alarmCreateKeys.get(id);
120 if (sKeys == null) { 123 if (sKeys == null) {
121 return Collections.emptySet(); 124 return Collections.emptySet();
122 } else { 125 } else {
123 - Set<EntityKey> keys = sKeys.get(severity); 126 + Set<AlarmConditionFilterKey> keys = sKeys.get(severity);
124 if (keys == null) { 127 if (keys == null) {
125 return Collections.emptySet(); 128 return Collections.emptySet();
126 } else { 129 } else {
@@ -129,8 +132,8 @@ class ProfileState { @@ -129,8 +132,8 @@ class ProfileState {
129 } 132 }
130 } 133 }
131 134
132 - Set<EntityKey> getClearAlarmKeys(String id) {  
133 - Set<EntityKey> keys = alarmClearKeys.get(id); 135 + Set<AlarmConditionFilterKey> getClearAlarmKeys(String id) {
  136 + Set<AlarmConditionFilterKey> keys = alarmClearKeys.get(id);
134 if (keys == null) { 137 if (keys == null) {
135 return Collections.emptySet(); 138 return Collections.emptySet();
136 } else { 139 } else {
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.rule.engine.profile; 16 package org.thingsboard.rule.engine.profile;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
  19 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  20 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
19 import org.thingsboard.server.common.data.query.EntityKey; 21 import org.thingsboard.server.common.data.query.EntityKey;
20 import org.thingsboard.server.common.data.query.EntityKeyType; 22 import org.thingsboard.server.common.data.query.EntityKeyType;
21 23
@@ -24,11 +26,11 @@ import java.util.Set; @@ -24,11 +26,11 @@ import java.util.Set;
24 class SnapshotUpdate { 26 class SnapshotUpdate {
25 27
26 @Getter 28 @Getter
27 - private final EntityKeyType type; 29 + private final AlarmConditionKeyType type;
28 @Getter 30 @Getter
29 - private final Set<EntityKey> keys; 31 + private final Set<AlarmConditionFilterKey> keys;
30 32
31 - SnapshotUpdate(EntityKeyType type, Set<EntityKey> keys) { 33 + SnapshotUpdate(AlarmConditionKeyType type, Set<AlarmConditionFilterKey> keys) {
32 this.type = type; 34 this.type = type;
33 this.keys = keys; 35 this.keys = keys;
34 } 36 }
@@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
36 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
37 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 import org.thingsboard.server.common.data.device.profile.AlarmCondition; 38 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  39 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
  40 +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
  41 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
39 import org.thingsboard.server.common.data.device.profile.AlarmRule; 42 import org.thingsboard.server.common.data.device.profile.AlarmRule;
40 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 43 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
41 import org.thingsboard.server.common.data.device.profile.DeviceProfileData; 44 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
@@ -44,6 +47,7 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -44,6 +47,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
44 import org.thingsboard.server.common.data.id.DeviceProfileId; 47 import org.thingsboard.server.common.data.id.DeviceProfileId;
45 import org.thingsboard.server.common.data.id.TenantId; 48 import org.thingsboard.server.common.data.id.TenantId;
46 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 49 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  50 +import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
47 import org.thingsboard.server.common.data.query.DynamicValue; 51 import org.thingsboard.server.common.data.query.DynamicValue;
48 import org.thingsboard.server.common.data.query.DynamicValueSourceType; 52 import org.thingsboard.server.common.data.query.DynamicValueSourceType;
49 import org.thingsboard.server.common.data.query.EntityKey; 53 import org.thingsboard.server.common.data.query.EntityKey;
@@ -62,6 +66,7 @@ import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; @@ -62,6 +66,7 @@ import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
62 import org.thingsboard.server.dao.model.sql.AttributeKvEntity; 66 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
63 import org.thingsboard.server.dao.timeseries.TimeseriesService; 67 import org.thingsboard.server.dao.timeseries.TimeseriesService;
64 68
  69 +import java.util.Arrays;
65 import java.util.Collections; 70 import java.util.Collections;
66 import java.util.List; 71 import java.util.List;
67 import java.util.Optional; 72 import java.util.Optional;
@@ -69,6 +74,7 @@ import java.util.TreeMap; @@ -69,6 +74,7 @@ import java.util.TreeMap;
69 import java.util.UUID; 74 import java.util.UUID;
70 75
71 import static org.mockito.ArgumentMatchers.eq; 76 import static org.mockito.ArgumentMatchers.eq;
  77 +import static org.mockito.Mockito.mock;
72 import static org.mockito.Mockito.verify; 78 import static org.mockito.Mockito.verify;
73 79
74 @RunWith(MockitoJUnitRunner.class) 80 @RunWith(MockitoJUnitRunner.class)
@@ -141,8 +147,8 @@ public class TbDeviceProfileNodeTest { @@ -141,8 +147,8 @@ public class TbDeviceProfileNodeTest {
141 DeviceProfile deviceProfile = new DeviceProfile(); 147 DeviceProfile deviceProfile = new DeviceProfile();
142 DeviceProfileData deviceProfileData = new DeviceProfileData(); 148 DeviceProfileData deviceProfileData = new DeviceProfileData();
143 149
144 - KeyFilter highTempFilter = new KeyFilter();  
145 - highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 150 + AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
  151 + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
146 highTempFilter.setValueType(EntityKeyValueType.NUMERIC); 152 highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
147 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); 153 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
148 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 154 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -157,8 +163,8 @@ public class TbDeviceProfileNodeTest { @@ -157,8 +163,8 @@ public class TbDeviceProfileNodeTest {
157 dpa.setAlarmType("highTemperatureAlarm"); 163 dpa.setAlarmType("highTemperatureAlarm");
158 dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); 164 dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
159 165
160 - KeyFilter lowTempFilter = new KeyFilter();  
161 - lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 166 + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
  167 + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
162 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); 168 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
163 NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate(); 169 NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate();
164 lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); 170 lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
@@ -204,6 +210,175 @@ public class TbDeviceProfileNodeTest { @@ -204,6 +210,175 @@ public class TbDeviceProfileNodeTest {
204 } 210 }
205 211
206 @Test 212 @Test
  213 + public void testConstantKeyFilterSimple() throws Exception {
  214 + init();
  215 +
  216 + DeviceProfile deviceProfile = new DeviceProfile();
  217 + deviceProfile.setId(deviceProfileId);
  218 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  219 +
  220 + Device device = new Device();
  221 + device.setId(deviceId);
  222 + device.setCustomerId(customerId);
  223 +
  224 + AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey(
  225 + EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarmEnabled"
  226 + );
  227 +
  228 + AttributeKvEntity attributeKvEntity = new AttributeKvEntity();
  229 + attributeKvEntity.setId(compositeKey);
  230 + attributeKvEntity.setBooleanValue(Boolean.TRUE);
  231 + attributeKvEntity.setLastUpdateTs(System.currentTimeMillis());
  232 +
  233 + AttributeKvEntry entry = attributeKvEntity.toData();
  234 + ListenableFuture<List<AttributeKvEntry>> attrListListenableFuture = Futures.immediateFuture(Collections.singletonList(entry));
  235 +
  236 + AlarmConditionFilter alarmEnabledFilter = new AlarmConditionFilter();
  237 + alarmEnabledFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.CONSTANT, "alarmEnabled"));
  238 + alarmEnabledFilter.setValue(Boolean.TRUE);
  239 + alarmEnabledFilter.setValueType(EntityKeyValueType.BOOLEAN);
  240 + BooleanFilterPredicate alarmEnabledPredicate = new BooleanFilterPredicate();
  241 + alarmEnabledPredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
  242 + alarmEnabledPredicate.setValue(new FilterPredicateValue<>(
  243 + Boolean.FALSE,
  244 + null,
  245 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarmEnabled")
  246 + ));
  247 + alarmEnabledFilter.setPredicate(alarmEnabledPredicate);
  248 +
  249 + AlarmConditionFilter temperatureFilter = new AlarmConditionFilter();
  250 + temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
  251 + temperatureFilter.setValueType(EntityKeyValueType.NUMERIC);
  252 + NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate();
  253 + temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  254 + temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null));
  255 + temperatureFilter.setPredicate(temperaturePredicate);
  256 +
  257 + AlarmCondition alarmCondition = new AlarmCondition();
  258 + alarmCondition.setCondition(Arrays.asList(alarmEnabledFilter, temperatureFilter));
  259 + AlarmRule alarmRule = new AlarmRule();
  260 + alarmRule.setCondition(alarmCondition);
  261 + DeviceProfileAlarm dpa = new DeviceProfileAlarm();
  262 + dpa.setId("alarmEnabledAlarmID");
  263 + dpa.setAlarmType("alarmEnabledAlarm");
  264 + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
  265 +
  266 + deviceProfileData.setAlarms(Collections.singletonList(dpa));
  267 + deviceProfile.setProfileData(deviceProfileData);
  268 +
  269 + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile);
  270 + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature")))
  271 + .thenReturn(Futures.immediateFuture(Collections.emptyList()));
  272 + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm"))
  273 + .thenReturn(Futures.immediateFuture(null));
  274 + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg());
  275 + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
  276 + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
  277 + .thenReturn(attrListListenableFuture);
  278 +
  279 + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
  280 + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString()))
  281 + .thenReturn(theMsg);
  282 +
  283 + ObjectNode data = mapper.createObjectNode();
  284 + data.put("temperature", 21);
  285 + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(),
  286 + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null);
  287 +
  288 + node.onMsg(ctx, msg);
  289 + verify(ctx).tellSuccess(msg);
  290 + verify(ctx).tellNext(theMsg, "Alarm Created");
  291 + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
  292 + }
  293 +
  294 + @Test
  295 + public void testConstantKeyFilterInherited() throws Exception {
  296 + init();
  297 +
  298 + DeviceProfile deviceProfile = new DeviceProfile();
  299 + deviceProfile.setId(deviceProfileId);
  300 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  301 +
  302 + Device device = new Device();
  303 + device.setId(deviceId);
  304 + device.setCustomerId(customerId);
  305 +
  306 + AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey(
  307 + EntityType.TENANT, tenantId.getId(), "SERVER_SCOPE", "alarmEnabled"
  308 + );
  309 +
  310 + AttributeKvEntity attributeKvEntity = new AttributeKvEntity();
  311 + attributeKvEntity.setId(compositeKey);
  312 + attributeKvEntity.setBooleanValue(Boolean.TRUE);
  313 + attributeKvEntity.setLastUpdateTs(System.currentTimeMillis());
  314 +
  315 + AttributeKvEntry entry = attributeKvEntity.toData();
  316 + ListenableFuture<Optional<AttributeKvEntry>> attrListListenableFuture = Futures.immediateFuture(Optional.of(entry));
  317 +
  318 + AlarmConditionFilter alarmEnabledFilter = new AlarmConditionFilter();
  319 + alarmEnabledFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.CONSTANT, "alarmEnabled"));
  320 + alarmEnabledFilter.setValue(Boolean.TRUE);
  321 + alarmEnabledFilter.setValueType(EntityKeyValueType.BOOLEAN);
  322 + BooleanFilterPredicate alarmEnabledPredicate = new BooleanFilterPredicate();
  323 + alarmEnabledPredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
  324 + alarmEnabledPredicate.setValue(new FilterPredicateValue<>(
  325 + Boolean.FALSE,
  326 + null,
  327 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarmEnabled", true)
  328 + ));
  329 + alarmEnabledFilter.setPredicate(alarmEnabledPredicate);
  330 +
  331 + AlarmConditionFilter temperatureFilter = new AlarmConditionFilter();
  332 + temperatureFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
  333 + temperatureFilter.setValueType(EntityKeyValueType.NUMERIC);
  334 + NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate();
  335 + temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  336 + temperaturePredicate.setValue(new FilterPredicateValue<>(20.0, null, null));
  337 + temperatureFilter.setPredicate(temperaturePredicate);
  338 +
  339 + AlarmCondition alarmCondition = new AlarmCondition();
  340 + alarmCondition.setCondition(Arrays.asList(alarmEnabledFilter, temperatureFilter));
  341 + AlarmRule alarmRule = new AlarmRule();
  342 + alarmRule.setCondition(alarmCondition);
  343 + DeviceProfileAlarm dpa = new DeviceProfileAlarm();
  344 + dpa.setId("alarmEnabledAlarmID");
  345 + dpa.setAlarmType("alarmEnabledAlarm");
  346 + dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)));
  347 +
  348 + deviceProfileData.setAlarms(Collections.singletonList(dpa));
  349 + deviceProfile.setProfileData(deviceProfileData);
  350 +
  351 + Mockito.when(deviceService.findDeviceById(tenantId, deviceId)).thenReturn(device);
  352 + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile);
  353 + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature")))
  354 + .thenReturn(Futures.immediateFuture(Collections.emptyList()));
  355 + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm"))
  356 + .thenReturn(Futures.immediateFuture(null));
  357 + Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg());
  358 + Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
  359 + Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
  360 + .thenReturn(Futures.immediateFuture(Collections.emptyList()));
  361 + Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString()))
  362 + .thenReturn(Futures.immediateFuture(Optional.empty()));
  363 + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString()))
  364 + .thenReturn(attrListListenableFuture);
  365 +
  366 + TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
  367 + Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.anyString()))
  368 + .thenReturn(theMsg);
  369 +
  370 + ObjectNode data = mapper.createObjectNode();
  371 + data.put("temperature", 21);
  372 + TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(),
  373 + TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null);
  374 +
  375 + node.onMsg(ctx, msg);
  376 + verify(ctx).tellSuccess(msg);
  377 + verify(ctx).tellNext(theMsg, "Alarm Created");
  378 + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
  379 + }
  380 +
  381 + @Test
207 public void testCurrentDeviceAttributeForDynamicValue() throws Exception { 382 public void testCurrentDeviceAttributeForDynamicValue() throws Exception {
208 init(); 383 init();
209 384
@@ -228,8 +403,8 @@ public class TbDeviceProfileNodeTest { @@ -228,8 +403,8 @@ public class TbDeviceProfileNodeTest {
228 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess = 403 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
229 Futures.immediateFuture(Collections.singletonList(entry)); 404 Futures.immediateFuture(Collections.singletonList(entry));
230 405
231 - KeyFilter highTempFilter = new KeyFilter();  
232 - highTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 406 + AlarmConditionFilter highTempFilter = new AlarmConditionFilter();
  407 + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
233 highTempFilter.setValueType(EntityKeyValueType.NUMERIC); 408 highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
234 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); 409 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
235 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 410 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -303,8 +478,8 @@ public class TbDeviceProfileNodeTest { @@ -303,8 +478,8 @@ public class TbDeviceProfileNodeTest {
303 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess = 478 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
304 Futures.immediateFuture(Optional.of(entry)); 479 Futures.immediateFuture(Optional.of(entry));
305 480
306 - KeyFilter lowTempFilter = new KeyFilter();  
307 - lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 481 + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
  482 + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
308 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); 483 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
309 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); 484 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
310 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); 485 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
@@ -382,8 +557,8 @@ public class TbDeviceProfileNodeTest { @@ -382,8 +557,8 @@ public class TbDeviceProfileNodeTest {
382 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess = 557 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
383 Futures.immediateFuture(Optional.of(entry)); 558 Futures.immediateFuture(Optional.of(entry));
384 559
385 - KeyFilter lowTempFilter = new KeyFilter();  
386 - lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 560 + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
  561 + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
387 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); 562 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
388 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); 563 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
389 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); 564 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
@@ -416,7 +591,7 @@ public class TbDeviceProfileNodeTest { @@ -416,7 +591,7 @@ public class TbDeviceProfileNodeTest {
416 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); 591 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
417 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) 592 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
418 .thenReturn(listListenableFutureWithLess); 593 .thenReturn(listListenableFutureWithLess);
419 - Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) 594 + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString()))
420 .thenReturn(optionalListenableFutureWithLess); 595 .thenReturn(optionalListenableFutureWithLess);
421 596
422 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); 597 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
@@ -454,8 +629,8 @@ public class TbDeviceProfileNodeTest { @@ -454,8 +629,8 @@ public class TbDeviceProfileNodeTest {
454 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess = 629 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
455 Futures.immediateFuture(Collections.singletonList(entry)); 630 Futures.immediateFuture(Collections.singletonList(entry));
456 631
457 - KeyFilter lowTempFilter = new KeyFilter();  
458 - lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 632 + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
  633 + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
459 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); 634 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
460 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); 635 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
461 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 636 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -502,9 +677,9 @@ public class TbDeviceProfileNodeTest { @@ -502,9 +677,9 @@ public class TbDeviceProfileNodeTest {
502 verify(ctx).tellSuccess(msg); 677 verify(ctx).tellSuccess(msg);
503 verify(ctx).tellNext(theMsg, "Alarm Created"); 678 verify(ctx).tellNext(theMsg, "Alarm Created");
504 verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); 679 verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
505 -  
506 } 680 }
507 681
  682 +
508 @Test 683 @Test
509 public void testCustomerInheritModeForDynamicValues() throws Exception { 684 public void testCustomerInheritModeForDynamicValues() throws Exception {
510 init(); 685 init();
@@ -527,8 +702,8 @@ public class TbDeviceProfileNodeTest { @@ -527,8 +702,8 @@ public class TbDeviceProfileNodeTest {
527 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess = 702 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
528 Futures.immediateFuture(Optional.of(entry)); 703 Futures.immediateFuture(Optional.of(entry));
529 704
530 - KeyFilter lowTempFilter = new KeyFilter();  
531 - lowTempFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); 705 + AlarmConditionFilter lowTempFilter = new AlarmConditionFilter();
  706 + lowTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature"));
532 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC); 707 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
533 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate(); 708 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
534 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); 709 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
@@ -561,7 +736,7 @@ public class TbDeviceProfileNodeTest { @@ -561,7 +736,7 @@ public class TbDeviceProfileNodeTest {
561 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); 736 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
562 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) 737 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
563 .thenReturn(listListenableFutureWithLess); 738 .thenReturn(listListenableFutureWithLess);
564 - Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) 739 + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString()))
565 .thenReturn(optionalListenableFutureWithLess); 740 .thenReturn(optionalListenableFutureWithLess);
566 741
567 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); 742 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");