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 36 import org.thingsboard.server.common.data.User;
37 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 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 42 import org.thingsboard.server.common.data.device.profile.AlarmRule;
40 43 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
41 44 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
... ... @@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
290 293 AlarmCondition temperatureCondition = new AlarmCondition();
291 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 298 temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
296 299 BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
297 300 temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
298 301 temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
299 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 306 temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
304 307 NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
305 308 temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
317 320 AlarmCondition clearTemperatureCondition = new AlarmCondition();
318 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 325 clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
323 326 NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
324 327 clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
... ... @@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
340 343 AlarmCondition humidityCondition = new AlarmCondition();
341 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 348 humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
346 349 BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
347 350 humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
348 351 humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
349 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 356 humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
354 357 NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
355 358 humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
... ... @@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
368 371 AlarmCondition clearHumidityCondition = new AlarmCondition();
369 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 376 clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
374 377 NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
375 378 clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
... ...
... ... @@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
26 26 @JsonIgnoreProperties(ignoreUnknown = true)
27 27 public class AlarmCondition {
28 28
29   - private List<KeyFilter> condition;
  29 + private List<AlarmConditionFilter> condition;
30 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 16 package org.thingsboard.rule.engine.profile;
17 17
18 18 import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
19 20 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
20 21 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
21 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 26 import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
23 27 import org.thingsboard.server.common.data.device.profile.AlarmRule;
24 28 import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
... ... @@ -45,6 +49,7 @@ import java.util.Set;
45 49 import java.util.function.Function;
46 50
47 51 @Data
  52 +@Slf4j
48 53 class AlarmRuleState {
49 54
50 55 private final AlarmSeverity severity;
... ... @@ -52,12 +57,12 @@ class AlarmRuleState {
52 57 private final AlarmConditionSpec spec;
53 58 private final long requiredDurationInMs;
54 59 private final long requiredRepeats;
55   - private final Set<EntityKey> entityKeys;
  60 + private final Set<AlarmConditionFilterKey> entityKeys;
56 61 private PersistedAlarmRuleState state;
57 62 private boolean updateFlag;
58 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 66 this.severity = severity;
62 67 this.alarmRule = alarmRule;
63 68 this.entityKeys = entityKeys;
... ... @@ -84,8 +89,8 @@ class AlarmRuleState {
84 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 94 if (entityKeys.contains(key)) {
90 95 return true;
91 96 }
... ... @@ -93,18 +98,14 @@ class AlarmRuleState {
93 98 return false;
94 99 }
95 100
96   - public boolean validateAttrUpdate(Set<EntityKey> changedKeys) {
  101 + public boolean validateAttrUpdate(Set<AlarmConditionFilterKey> changedKeys) {
97 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 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 109 if (entityKeys.contains(key)) {
109 110 return true;
110 111 }
... ... @@ -259,16 +260,46 @@ class AlarmRuleState {
259 260
260 261 private boolean eval(AlarmCondition condition, DataSnapshot data) {
261 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 275 if (value == null) {
265 276 return false;
266 277 }
267   - eval = eval && eval(data, value, keyFilter.getPredicate());
  278 + eval = eval && eval(data, value, filter.getPredicate());
268 279 }
269 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 303 private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) {
273 304 switch (predicate.getType()) {
274 305 case STRING:
... ... @@ -389,23 +420,14 @@ class AlarmRuleState {
389 420 if (value.getDynamicValue() != null) {
390 421 switch (value.getDynamicValue().getSourceType()) {
391 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 425 break;
404 426 }
405 427 case CURRENT_CUSTOMER:
406 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 432 case CURRENT_TENANT:
411 433 ekv = dynamicPredicateValueCtx.getTenantValue(value.getDynamicValue().getSourceAttribute());
... ...
... ... @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Tenant;
29 29 import org.thingsboard.server.common.data.alarm.Alarm;
30 30 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
31 31 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  32 +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
32 33 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
33 34 import org.thingsboard.server.common.data.id.CustomerId;
34 35 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -138,9 +139,9 @@ class AlarmState {
138 139 public boolean validateUpdate(SnapshotUpdate update, AlarmRuleState state) {
139 140 if (update != null) {
140 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 143 return state.validateTsUpdate(update.getKeys());
143   - } else if (update.getType().equals(EntityKeyType.ATTRIBUTE)) {
  144 + } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
144 145 return state.validateAttrUpdate(update.getKeys());
145 146 }
146 147 }
... ... @@ -252,7 +253,7 @@ class AlarmState {
252 253 String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
253 254
254 255 if (StringUtils.isNotEmpty(alarmDetailsStr)) {
255   - for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
  256 + for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
256 257 EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
257 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 17
18 18 import lombok.Getter;
19 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 22 import org.thingsboard.server.common.data.query.EntityKey;
21 23 import org.thingsboard.server.common.data.query.EntityKeyType;
22 24
... ... @@ -30,55 +32,42 @@ class DataSnapshot {
30 32 @Getter
31 33 @Setter
32 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 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 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 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 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 71 if (keys.contains(key)) {
83 72 EntityKeyValue oldValue = values.put(key, value);
84 73 if (updateOfTs) {
... ... @@ -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 26 import org.thingsboard.server.common.data.Device;
27 27 import org.thingsboard.server.common.data.DeviceProfile;
28 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 31 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
30 32 import org.thingsboard.server.common.data.id.DeviceId;
31 33 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -97,10 +99,10 @@ class DeviceState {
97 99 }
98 100
99 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 103 this.deviceProfile.updateDeviceProfile(deviceProfile);
102 104 if (latestValues != null) {
103   - Set<EntityKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
  105 + Set<AlarmConditionFilterKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
104 106 keysToFetch.removeAll(oldKeys);
105 107 if (!keysToFetch.isEmpty()) {
106 108 addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
... ... @@ -260,29 +262,29 @@ class DeviceState {
260 262 }
261 263
262 264 private SnapshotUpdate merge(DataSnapshot latestValues, Long newTs, List<KvEntry> data) {
263   - Set<EntityKey> keys = new HashSet<>();
  265 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
264 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 268 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
267 269 keys.add(entityKey);
268 270 }
269 271 }
270 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 276 private SnapshotUpdate merge(DataSnapshot latestValues, Set<AttributeKvEntry> attributes, String scope) {
275 277 long newTs = 0;
276   - Set<EntityKey> keys = new HashSet<>();
  278 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
277 279 for (AttributeKvEntry entry : attributes) {
278 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 282 if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
281 283 keys.add(entityKey);
282 284 }
283 285 }
284 286 latestValues.setTs(newTs);
285   - return new SnapshotUpdate(EntityKeyType.ATTRIBUTE, keys);
  287 + return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys);
286 288 }
287 289
288 290 private static EntityKeyType getKeyTypeFromScope(String scope) {
... ... @@ -298,37 +300,22 @@ class DeviceState {
298 300 }
299 301
300 302 private DataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException {
301   - Set<EntityKey> entityKeysToFetch = deviceProfile.getEntityKeys();
  303 + Set<AlarmConditionFilterKey> entityKeysToFetch = deviceProfile.getEntityKeys();
302 304 DataSnapshot result = new DataSnapshot(entityKeysToFetch);
303 305 addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
304 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 311 Set<String> latestTsKeys = new HashSet<>();
313 312
314 313 Device device = null;
315   - for (EntityKey entityKey : entityKeysToFetch) {
  314 + for (AlarmConditionFilterKey entityKey : entityKeysToFetch) {
316 315 String key = entityKey.getKey();
317 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 317 case ATTRIBUTE:
328   - serverAttributeKeys.add(key);
329   - clientAttributeKeys.add(key);
330   - sharedAttributeKeys.add(key);
331   - commonAttributeKeys.add(key);
  318 + attributeKeys.add(key);
332 319 break;
333 320 case TIME_SERIES:
334 321 latestTsKeys.add(key);
... ... @@ -361,32 +348,22 @@ class DeviceState {
361 348 List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get();
362 349 for (TsKvEntry entry : data) {
363 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 363 for (AttributeKvEntry entry : data) {
384 364 if (entry.getValue() != null) {
385 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 19 import lombok.Getter;
20 20 import org.thingsboard.server.common.data.DeviceProfile;
21 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 25 import org.thingsboard.server.common.data.device.profile.AlarmRule;
23 26 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
24 27 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -50,10 +53,10 @@ class ProfileState {
50 53 @Getter(AccessLevel.PACKAGE)
51 54 private final List<DeviceProfileAlarm> alarmSettings = new CopyOnWriteArrayList<>();
52 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 61 ProfileState(DeviceProfile deviceProfile) {
59 62 updateDeviceProfile(deviceProfile);
... ... @@ -68,18 +71,18 @@ class ProfileState {
68 71 if (deviceProfile.getProfileData().getAlarms() != null) {
69 72 alarmSettings.addAll(deviceProfile.getProfileData().getAlarms());
70 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 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 78 entityKeys.add(keyFilter.getKey());
76 79 ruleKeys.add(keyFilter.getKey());
77 80 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys);
78 81 }
79 82 }));
80 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 86 entityKeys.add(keyFilter.getKey());
84 87 clearAlarmKeys.add(keyFilter.getKey());
85 88 addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys);
... ... @@ -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 96 switch (predicate.getType()) {
94 97 case STRING:
95 98 case NUMERIC:
... ... @@ -98,7 +101,7 @@ class ProfileState {
98 101 if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
99 102 value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
100 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 105 entityKeys.add(entityKey);
103 106 ruleKeys.add(entityKey);
104 107 }
... ... @@ -115,12 +118,12 @@ class ProfileState {
115 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 123 if (sKeys == null) {
121 124 return Collections.emptySet();
122 125 } else {
123   - Set<EntityKey> keys = sKeys.get(severity);
  126 + Set<AlarmConditionFilterKey> keys = sKeys.get(severity);
124 127 if (keys == null) {
125 128 return Collections.emptySet();
126 129 } else {
... ... @@ -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 137 if (keys == null) {
135 138 return Collections.emptySet();
136 139 } else {
... ...
... ... @@ -16,6 +16,8 @@
16 16 package org.thingsboard.rule.engine.profile;
17 17
18 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 21 import org.thingsboard.server.common.data.query.EntityKey;
20 22 import org.thingsboard.server.common.data.query.EntityKeyType;
21 23
... ... @@ -24,11 +26,11 @@ import java.util.Set;
24 26 class SnapshotUpdate {
25 27
26 28 @Getter
27   - private final EntityKeyType type;
  29 + private final AlarmConditionKeyType type;
28 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 34 this.type = type;
33 35 this.keys = keys;
34 36 }
... ...
... ... @@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.DeviceProfile;
36 36 import org.thingsboard.server.common.data.EntityType;
37 37 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
38 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 42 import org.thingsboard.server.common.data.device.profile.AlarmRule;
40 43 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
41 44 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
... ... @@ -44,6 +47,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
44 47 import org.thingsboard.server.common.data.id.DeviceProfileId;
45 48 import org.thingsboard.server.common.data.id.TenantId;
46 49 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  50 +import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
47 51 import org.thingsboard.server.common.data.query.DynamicValue;
48 52 import org.thingsboard.server.common.data.query.DynamicValueSourceType;
49 53 import org.thingsboard.server.common.data.query.EntityKey;
... ... @@ -62,6 +66,7 @@ import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
62 66 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
63 67 import org.thingsboard.server.dao.timeseries.TimeseriesService;
64 68
  69 +import java.util.Arrays;
65 70 import java.util.Collections;
66 71 import java.util.List;
67 72 import java.util.Optional;
... ... @@ -69,6 +74,7 @@ import java.util.TreeMap;
69 74 import java.util.UUID;
70 75
71 76 import static org.mockito.ArgumentMatchers.eq;
  77 +import static org.mockito.Mockito.mock;
72 78 import static org.mockito.Mockito.verify;
73 79
74 80 @RunWith(MockitoJUnitRunner.class)
... ... @@ -141,8 +147,8 @@ public class TbDeviceProfileNodeTest {
141 147 DeviceProfile deviceProfile = new DeviceProfile();
142 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 152 highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
147 153 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
148 154 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -157,8 +163,8 @@ public class TbDeviceProfileNodeTest {
157 163 dpa.setAlarmType("highTemperatureAlarm");
158 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 168 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
163 169 NumericFilterPredicate lowTemperaturePredicate = new NumericFilterPredicate();
164 170 lowTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
... ... @@ -204,6 +210,175 @@ public class TbDeviceProfileNodeTest {
204 210 }
205 211
206 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 382 public void testCurrentDeviceAttributeForDynamicValue() throws Exception {
208 383 init();
209 384
... ... @@ -228,8 +403,8 @@ public class TbDeviceProfileNodeTest {
228 403 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
229 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 408 highTempFilter.setValueType(EntityKeyValueType.NUMERIC);
234 409 NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate();
235 410 highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -303,8 +478,8 @@ public class TbDeviceProfileNodeTest {
303 478 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
304 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 483 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
309 484 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
310 485 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
... ... @@ -382,8 +557,8 @@ public class TbDeviceProfileNodeTest {
382 557 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
383 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 562 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
388 563 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
389 564 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
... ... @@ -416,7 +591,7 @@ public class TbDeviceProfileNodeTest {
416 591 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
417 592 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
418 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 595 .thenReturn(optionalListenableFutureWithLess);
421 596
422 597 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
... ... @@ -454,8 +629,8 @@ public class TbDeviceProfileNodeTest {
454 629 ListenableFuture<List<AttributeKvEntry>> listListenableFutureWithLess =
455 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 634 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
460 635 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
461 636 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -502,9 +677,9 @@ public class TbDeviceProfileNodeTest {
502 677 verify(ctx).tellSuccess(msg);
503 678 verify(ctx).tellNext(theMsg, "Alarm Created");
504 679 verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any());
505   -
506 680 }
507 681
  682 +
508 683 @Test
509 684 public void testCustomerInheritModeForDynamicValues() throws Exception {
510 685 init();
... ... @@ -527,8 +702,8 @@ public class TbDeviceProfileNodeTest {
527 702 ListenableFuture<Optional<AttributeKvEntry>> optionalListenableFutureWithLess =
528 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 707 lowTempFilter.setValueType(EntityKeyValueType.NUMERIC);
533 708 NumericFilterPredicate lowTempPredicate = new NumericFilterPredicate();
534 709 lowTempPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -561,7 +736,7 @@ public class TbDeviceProfileNodeTest {
561 736 Mockito.when(ctx.getAttributesService()).thenReturn(attributesService);
562 737 Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet()))
563 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 740 .thenReturn(optionalListenableFutureWithLess);
566 741
567 742 TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "");
... ...