Commit 3cd964327a2686f6f2f6db4b8cccdd67b20a10dc
Committed by
Andrew Shvayka
1 parent
72374979
Constant filters for device profile
Showing
12 changed files
with
414 additions
and
181 deletions
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -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()); |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -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(), ""); |