Commit 6d185f1c01090c3180b7312f1fdfbd258f8ba8ba
1 parent
c4625177
Improvements to device profile rule node
Showing
13 changed files
with
211 additions
and
89 deletions
@@ -469,6 +469,14 @@ class DefaultTbContext implements TbContext { | @@ -469,6 +469,14 @@ class DefaultTbContext implements TbContext { | ||
469 | return mainCtx.getRuleNodeStateService().save(getTenantId(), state); | 469 | return mainCtx.getRuleNodeStateService().save(getTenantId(), state); |
470 | } | 470 | } |
471 | 471 | ||
472 | + @Override | ||
473 | + public void clearRuleNodeStates() { | ||
474 | + if (log.isDebugEnabled()) { | ||
475 | + log.debug("[{}][{}] Going to clear rule node states", getTenantId(), getSelfId()); | ||
476 | + } | ||
477 | + mainCtx.getRuleNodeStateService().removeByRuleNodeId(getTenantId(), getSelfId()); | ||
478 | + } | ||
479 | + | ||
472 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { | 480 | private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { |
473 | TbMsgMetaData metaData = new TbMsgMetaData(); | 481 | TbMsgMetaData metaData = new TbMsgMetaData(); |
474 | metaData.putValue("ruleNodeId", ruleNodeId.toString()); | 482 | metaData.putValue("ruleNodeId", ruleNodeId.toString()); |
@@ -164,6 +164,8 @@ public class RuleChainController extends BaseController { | @@ -164,6 +164,8 @@ public class RuleChainController extends BaseController { | ||
164 | 164 | ||
165 | RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); | 165 | RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); |
166 | 166 | ||
167 | + tbClusterService.onEntityStateChange(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); | ||
168 | + | ||
167 | logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); | 169 | logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); |
168 | 170 | ||
169 | return savedRuleChain; | 171 | return savedRuleChain; |
@@ -30,4 +30,5 @@ public interface RuleNodeStateService { | @@ -30,4 +30,5 @@ public interface RuleNodeStateService { | ||
30 | 30 | ||
31 | RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState); | 31 | RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState); |
32 | 32 | ||
33 | + void removeByRuleNodeId(TenantId tenantId, RuleNodeId selfId); | ||
33 | } | 34 | } |
@@ -18,5 +18,6 @@ package org.thingsboard.server.common.data.query; | @@ -18,5 +18,6 @@ package org.thingsboard.server.common.data.query; | ||
18 | public enum DynamicValueSourceType { | 18 | public enum DynamicValueSourceType { |
19 | CURRENT_TENANT, | 19 | CURRENT_TENANT, |
20 | CURRENT_CUSTOMER, | 20 | CURRENT_CUSTOMER, |
21 | - CURRENT_USER | 21 | + CURRENT_USER, |
22 | + CURRENT_DEVICE | ||
22 | } | 23 | } |
@@ -68,6 +68,17 @@ public class BaseRuleNodeStateService extends AbstractEntityService implements R | @@ -68,6 +68,17 @@ public class BaseRuleNodeStateService extends AbstractEntityService implements R | ||
68 | return saveOrUpdate(tenantId, ruleNodeState, false); | 68 | return saveOrUpdate(tenantId, ruleNodeState, false); |
69 | } | 69 | } |
70 | 70 | ||
71 | + @Override | ||
72 | + public void removeByRuleNodeId(TenantId tenantId, RuleNodeId ruleNodeId) { | ||
73 | + if (tenantId == null) { | ||
74 | + throw new DataValidationException("Tenant id should be specified!."); | ||
75 | + } | ||
76 | + if (ruleNodeId == null) { | ||
77 | + throw new DataValidationException("Rule node id should be specified!."); | ||
78 | + } | ||
79 | + ruleNodeStateDao.removeByRuleNodeId(ruleNodeId.getId()); | ||
80 | + } | ||
81 | + | ||
71 | public RuleNodeState saveOrUpdate(TenantId tenantId, RuleNodeState ruleNodeState, boolean update) { | 82 | public RuleNodeState saveOrUpdate(TenantId tenantId, RuleNodeState ruleNodeState, boolean update) { |
72 | try { | 83 | try { |
73 | if (update) { | 84 | if (update) { |
@@ -16,6 +16,8 @@ | @@ -16,6 +16,8 @@ | ||
16 | package org.thingsboard.server.dao.rule; | 16 | package org.thingsboard.server.dao.rule; |
17 | 17 | ||
18 | import org.thingsboard.server.common.data.id.EntityId; | 18 | import org.thingsboard.server.common.data.id.EntityId; |
19 | +import org.thingsboard.server.common.data.id.RuleNodeId; | ||
20 | +import org.thingsboard.server.common.data.id.TenantId; | ||
19 | import org.thingsboard.server.common.data.page.PageData; | 21 | import org.thingsboard.server.common.data.page.PageData; |
20 | import org.thingsboard.server.common.data.page.PageLink; | 22 | import org.thingsboard.server.common.data.page.PageLink; |
21 | import org.thingsboard.server.common.data.rule.RuleNodeState; | 23 | import org.thingsboard.server.common.data.rule.RuleNodeState; |
@@ -31,4 +33,6 @@ public interface RuleNodeStateDao extends Dao<RuleNodeState> { | @@ -31,4 +33,6 @@ public interface RuleNodeStateDao extends Dao<RuleNodeState> { | ||
31 | PageData<RuleNodeState> findByRuleNodeId(UUID ruleNodeId, PageLink pageLink); | 33 | PageData<RuleNodeState> findByRuleNodeId(UUID ruleNodeId, PageLink pageLink); |
32 | 34 | ||
33 | RuleNodeState findByRuleNodeIdAndEntityId(UUID ruleNodeId, UUID entityId); | 35 | RuleNodeState findByRuleNodeIdAndEntityId(UUID ruleNodeId, UUID entityId); |
36 | + | ||
37 | + void removeByRuleNodeId(UUID ruleNodeId); | ||
34 | } | 38 | } |
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; | ||
19 | import org.springframework.beans.factory.annotation.Autowired; | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | import org.springframework.data.repository.CrudRepository; | 20 | import org.springframework.data.repository.CrudRepository; |
21 | import org.springframework.stereotype.Component; | 21 | import org.springframework.stereotype.Component; |
22 | +import org.springframework.transaction.annotation.Transactional; | ||
22 | import org.thingsboard.server.common.data.id.EntityId; | 23 | import org.thingsboard.server.common.data.id.EntityId; |
23 | import org.thingsboard.server.common.data.page.PageData; | 24 | import org.thingsboard.server.common.data.page.PageData; |
24 | import org.thingsboard.server.common.data.page.PageLink; | 25 | import org.thingsboard.server.common.data.page.PageLink; |
@@ -56,4 +57,10 @@ public class JpaRuleNodeStateDao extends JpaAbstractDao<RuleNodeStateEntity, Rul | @@ -56,4 +57,10 @@ public class JpaRuleNodeStateDao extends JpaAbstractDao<RuleNodeStateEntity, Rul | ||
56 | public RuleNodeState findByRuleNodeIdAndEntityId(UUID ruleNodeId, UUID entityId) { | 57 | public RuleNodeState findByRuleNodeIdAndEntityId(UUID ruleNodeId, UUID entityId) { |
57 | return DaoUtil.getData(ruleNodeStateRepository.findByRuleNodeIdAndEntityId(ruleNodeId, entityId)); | 58 | return DaoUtil.getData(ruleNodeStateRepository.findByRuleNodeIdAndEntityId(ruleNodeId, entityId)); |
58 | } | 59 | } |
60 | + | ||
61 | + @Transactional | ||
62 | + @Override | ||
63 | + public void removeByRuleNodeId(UUID ruleNodeId) { | ||
64 | + ruleNodeStateRepository.removeByRuleNodeId(ruleNodeId); | ||
65 | + } | ||
59 | } | 66 | } |
@@ -33,4 +33,7 @@ public interface RuleNodeStateRepository extends PagingAndSortingRepository<Rule | @@ -33,4 +33,7 @@ public interface RuleNodeStateRepository extends PagingAndSortingRepository<Rule | ||
33 | 33 | ||
34 | @Query("SELECT e FROM RuleNodeStateEntity e WHERE e.ruleNodeId = :ruleNodeId and e.entityId = :entityId") | 34 | @Query("SELECT e FROM RuleNodeStateEntity e WHERE e.ruleNodeId = :ruleNodeId and e.entityId = :entityId") |
35 | RuleNodeStateEntity findByRuleNodeIdAndEntityId(@Param("ruleNodeId") UUID ruleNodeId, @Param("entityId") UUID entityId); | 35 | RuleNodeStateEntity findByRuleNodeIdAndEntityId(@Param("ruleNodeId") UUID ruleNodeId, @Param("entityId") UUID entityId); |
36 | + | ||
37 | + void removeByRuleNodeId(@Param("ruleNodeId") UUID ruleNodeId); | ||
38 | + | ||
36 | } | 39 | } |
@@ -221,4 +221,6 @@ public interface TbContext { | @@ -221,4 +221,6 @@ public interface TbContext { | ||
221 | RuleNodeState findRuleNodeStateForEntity(EntityId entityId); | 221 | RuleNodeState findRuleNodeStateForEntity(EntityId entityId); |
222 | 222 | ||
223 | RuleNodeState saveRuleNodeState(RuleNodeState state); | 223 | RuleNodeState saveRuleNodeState(RuleNodeState state); |
224 | + | ||
225 | + void clearRuleNodeStates(); | ||
224 | } | 226 | } |
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.query.BooleanFilterPredicate; | @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.query.BooleanFilterPredicate; | ||
31 | import org.thingsboard.server.common.data.query.ComplexFilterPredicate; | 31 | import org.thingsboard.server.common.data.query.ComplexFilterPredicate; |
32 | import org.thingsboard.server.common.data.query.EntityKey; | 32 | import org.thingsboard.server.common.data.query.EntityKey; |
33 | import org.thingsboard.server.common.data.query.EntityKeyType; | 33 | import org.thingsboard.server.common.data.query.EntityKeyType; |
34 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | ||
34 | import org.thingsboard.server.common.data.query.KeyFilter; | 35 | import org.thingsboard.server.common.data.query.KeyFilter; |
35 | import org.thingsboard.server.common.data.query.KeyFilterPredicate; | 36 | import org.thingsboard.server.common.data.query.KeyFilterPredicate; |
36 | import org.thingsboard.server.common.data.query.NumericFilterPredicate; | 37 | import org.thingsboard.server.common.data.query.NumericFilterPredicate; |
@@ -41,6 +42,7 @@ import java.time.Instant; | @@ -41,6 +42,7 @@ import java.time.Instant; | ||
41 | import java.time.ZoneId; | 42 | import java.time.ZoneId; |
42 | import java.time.ZonedDateTime; | 43 | import java.time.ZonedDateTime; |
43 | import java.util.Set; | 44 | import java.util.Set; |
45 | +import java.util.function.Function; | ||
44 | 46 | ||
45 | @Data | 47 | @Data |
46 | class AlarmRuleState { | 48 | class AlarmRuleState { |
@@ -246,38 +248,38 @@ class AlarmRuleState { | @@ -246,38 +248,38 @@ class AlarmRuleState { | ||
246 | if (value == null) { | 248 | if (value == null) { |
247 | return false; | 249 | return false; |
248 | } | 250 | } |
249 | - eval = eval && eval(value, keyFilter.getPredicate()); | 251 | + eval = eval && eval(data, value, keyFilter.getPredicate()); |
250 | } | 252 | } |
251 | return eval; | 253 | return eval; |
252 | } | 254 | } |
253 | 255 | ||
254 | - private boolean eval(EntityKeyValue value, KeyFilterPredicate predicate) { | 256 | + private boolean eval(DataSnapshot data, EntityKeyValue value, KeyFilterPredicate predicate) { |
255 | switch (predicate.getType()) { | 257 | switch (predicate.getType()) { |
256 | case STRING: | 258 | case STRING: |
257 | - return evalStrPredicate(value, (StringFilterPredicate) predicate); | 259 | + return evalStrPredicate(data, value, (StringFilterPredicate) predicate); |
258 | case NUMERIC: | 260 | case NUMERIC: |
259 | - return evalNumPredicate(value, (NumericFilterPredicate) predicate); | ||
260 | - case COMPLEX: | ||
261 | - return evalComplexPredicate(value, (ComplexFilterPredicate) predicate); | 261 | + return evalNumPredicate(data, value, (NumericFilterPredicate) predicate); |
262 | case BOOLEAN: | 262 | case BOOLEAN: |
263 | - return evalBoolPredicate(value, (BooleanFilterPredicate) predicate); | 263 | + return evalBoolPredicate(data, value, (BooleanFilterPredicate) predicate); |
264 | + case COMPLEX: | ||
265 | + return evalComplexPredicate(data, value, (ComplexFilterPredicate) predicate); | ||
264 | default: | 266 | default: |
265 | return false; | 267 | return false; |
266 | } | 268 | } |
267 | } | 269 | } |
268 | 270 | ||
269 | - private boolean evalComplexPredicate(EntityKeyValue ekv, ComplexFilterPredicate predicate) { | 271 | + private boolean evalComplexPredicate(DataSnapshot data, EntityKeyValue ekv, ComplexFilterPredicate predicate) { |
270 | switch (predicate.getOperation()) { | 272 | switch (predicate.getOperation()) { |
271 | case OR: | 273 | case OR: |
272 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { | 274 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { |
273 | - if (eval(ekv, kfp)) { | 275 | + if (eval(data, ekv, kfp)) { |
274 | return true; | 276 | return true; |
275 | } | 277 | } |
276 | } | 278 | } |
277 | return false; | 279 | return false; |
278 | case AND: | 280 | case AND: |
279 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { | 281 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { |
280 | - if (!eval(ekv, kfp)) { | 282 | + if (!eval(data, ekv, kfp)) { |
281 | return false; | 283 | return false; |
282 | } | 284 | } |
283 | } | 285 | } |
@@ -287,109 +289,55 @@ class AlarmRuleState { | @@ -287,109 +289,55 @@ class AlarmRuleState { | ||
287 | } | 289 | } |
288 | } | 290 | } |
289 | 291 | ||
290 | - private boolean evalBoolPredicate(EntityKeyValue ekv, BooleanFilterPredicate predicate) { | ||
291 | - Boolean value; | ||
292 | - switch (ekv.getDataType()) { | ||
293 | - case LONG: | ||
294 | - value = ekv.getLngValue() > 0; | ||
295 | - break; | ||
296 | - case DOUBLE: | ||
297 | - value = ekv.getDblValue() > 0; | ||
298 | - break; | ||
299 | - case BOOLEAN: | ||
300 | - value = ekv.getBoolValue(); | ||
301 | - break; | ||
302 | - case STRING: | ||
303 | - try { | ||
304 | - value = Boolean.parseBoolean(ekv.getStrValue()); | ||
305 | - break; | ||
306 | - } catch (RuntimeException e) { | ||
307 | - return false; | ||
308 | - } | ||
309 | - case JSON: | ||
310 | - try { | ||
311 | - value = Boolean.parseBoolean(ekv.getJsonValue()); | ||
312 | - break; | ||
313 | - } catch (RuntimeException e) { | ||
314 | - return false; | ||
315 | - } | ||
316 | - default: | ||
317 | - return false; | ||
318 | - } | ||
319 | - if (value == null) { | 292 | + private boolean evalBoolPredicate(DataSnapshot data, EntityKeyValue ekv, BooleanFilterPredicate predicate) { |
293 | + Boolean val = getBoolValue(ekv); | ||
294 | + if (val == null) { | ||
320 | return false; | 295 | return false; |
321 | } | 296 | } |
297 | + Boolean predicateValue = getPredicateValue(data, predicate.getValue(), AlarmRuleState::getBoolValue); | ||
322 | switch (predicate.getOperation()) { | 298 | switch (predicate.getOperation()) { |
323 | case EQUAL: | 299 | case EQUAL: |
324 | - return value.equals(predicate.getValue().getDefaultValue()); | 300 | + return val.equals(predicateValue); |
325 | case NOT_EQUAL: | 301 | case NOT_EQUAL: |
326 | - return !value.equals(predicate.getValue().getDefaultValue()); | 302 | + return !val.equals(predicateValue); |
327 | default: | 303 | default: |
328 | throw new RuntimeException("Operation not supported: " + predicate.getOperation()); | 304 | throw new RuntimeException("Operation not supported: " + predicate.getOperation()); |
329 | } | 305 | } |
330 | } | 306 | } |
331 | 307 | ||
332 | - private boolean evalNumPredicate(EntityKeyValue ekv, NumericFilterPredicate predicate) { | ||
333 | - Double value; | ||
334 | - switch (ekv.getDataType()) { | ||
335 | - case LONG: | ||
336 | - value = ekv.getLngValue().doubleValue(); | ||
337 | - break; | ||
338 | - case DOUBLE: | ||
339 | - value = ekv.getDblValue(); | ||
340 | - break; | ||
341 | - case BOOLEAN: | ||
342 | - value = ekv.getBoolValue() ? 1.0 : 0.0; | ||
343 | - break; | ||
344 | - case STRING: | ||
345 | - try { | ||
346 | - value = Double.parseDouble(ekv.getStrValue()); | ||
347 | - break; | ||
348 | - } catch (RuntimeException e) { | ||
349 | - return false; | ||
350 | - } | ||
351 | - case JSON: | ||
352 | - try { | ||
353 | - value = Double.parseDouble(ekv.getJsonValue()); | ||
354 | - break; | ||
355 | - } catch (RuntimeException e) { | ||
356 | - return false; | ||
357 | - } | ||
358 | - default: | ||
359 | - return false; | ||
360 | - } | ||
361 | - if (value == null) { | 308 | + private boolean evalNumPredicate(DataSnapshot data, EntityKeyValue ekv, NumericFilterPredicate predicate) { |
309 | + Double val = getDblValue(ekv); | ||
310 | + if (val == null) { | ||
362 | return false; | 311 | return false; |
363 | } | 312 | } |
364 | - | ||
365 | - Double predicateValue = predicate.getValue().getDefaultValue(); | 313 | + Double predicateValue = getPredicateValue(data, predicate.getValue(), AlarmRuleState::getDblValue); |
366 | switch (predicate.getOperation()) { | 314 | switch (predicate.getOperation()) { |
367 | case NOT_EQUAL: | 315 | case NOT_EQUAL: |
368 | - return !value.equals(predicateValue); | 316 | + return !val.equals(predicateValue); |
369 | case EQUAL: | 317 | case EQUAL: |
370 | - return value.equals(predicateValue); | 318 | + return val.equals(predicateValue); |
371 | case GREATER: | 319 | case GREATER: |
372 | - return value > predicateValue; | 320 | + return val > predicateValue; |
373 | case GREATER_OR_EQUAL: | 321 | case GREATER_OR_EQUAL: |
374 | - return value >= predicateValue; | 322 | + return val >= predicateValue; |
375 | case LESS: | 323 | case LESS: |
376 | - return value < predicateValue; | 324 | + return val < predicateValue; |
377 | case LESS_OR_EQUAL: | 325 | case LESS_OR_EQUAL: |
378 | - return value <= predicateValue; | 326 | + return val <= predicateValue; |
379 | default: | 327 | default: |
380 | throw new RuntimeException("Operation not supported: " + predicate.getOperation()); | 328 | throw new RuntimeException("Operation not supported: " + predicate.getOperation()); |
381 | } | 329 | } |
382 | } | 330 | } |
383 | 331 | ||
384 | - private boolean evalStrPredicate(EntityKeyValue ekv, StringFilterPredicate predicate) { | ||
385 | - String val; | ||
386 | - String predicateValue; | 332 | + private boolean evalStrPredicate(DataSnapshot data, EntityKeyValue ekv, StringFilterPredicate predicate) { |
333 | + String val = getStrValue(ekv); | ||
334 | + if (val == null) { | ||
335 | + return false; | ||
336 | + } | ||
337 | + String predicateValue = getPredicateValue(data, predicate.getValue(), AlarmRuleState::getStrValue); | ||
387 | if (predicate.isIgnoreCase()) { | 338 | if (predicate.isIgnoreCase()) { |
388 | - val = ekv.getStrValue().toLowerCase(); | ||
389 | - predicateValue = predicate.getValue().getDefaultValue().toLowerCase(); | ||
390 | - } else { | ||
391 | - val = ekv.getStrValue(); | ||
392 | - predicateValue = predicate.getValue().getDefaultValue(); | 339 | + val = val.toLowerCase(); |
340 | + predicateValue = predicateValue.toLowerCase(); | ||
393 | } | 341 | } |
394 | switch (predicate.getOperation()) { | 342 | switch (predicate.getOperation()) { |
395 | case CONTAINS: | 343 | case CONTAINS: |
@@ -409,4 +357,99 @@ class AlarmRuleState { | @@ -409,4 +357,99 @@ class AlarmRuleState { | ||
409 | } | 357 | } |
410 | } | 358 | } |
411 | 359 | ||
360 | + private <T> T getPredicateValue(DataSnapshot data, FilterPredicateValue<T> value, Function<EntityKeyValue, T> transformFunction) { | ||
361 | + EntityKeyValue ekv = getDynamicPredicateValue(data, value); | ||
362 | + if (ekv != null) { | ||
363 | + T result = transformFunction.apply(ekv); | ||
364 | + if (result != null) { | ||
365 | + return result; | ||
366 | + } | ||
367 | + } | ||
368 | + return value.getDefaultValue(); | ||
369 | + } | ||
370 | + | ||
371 | + private <T> EntityKeyValue getDynamicPredicateValue(DataSnapshot data, FilterPredicateValue<T> value) { | ||
372 | + EntityKeyValue ekv = null; | ||
373 | + if (value.getDynamicValue() != null) { | ||
374 | + ekv = data.getValue(new EntityKey(EntityKeyType.ATTRIBUTE, value.getDynamicValue().getSourceAttribute())); | ||
375 | + if (ekv == null) { | ||
376 | + ekv = data.getValue(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, value.getDynamicValue().getSourceAttribute())); | ||
377 | + if (ekv == null) { | ||
378 | + ekv = data.getValue(new EntityKey(EntityKeyType.SHARED_ATTRIBUTE, value.getDynamicValue().getSourceAttribute())); | ||
379 | + if (ekv == null) { | ||
380 | + ekv = data.getValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, value.getDynamicValue().getSourceAttribute())); | ||
381 | + } | ||
382 | + } | ||
383 | + } | ||
384 | + } | ||
385 | + return ekv; | ||
386 | + } | ||
387 | + | ||
388 | + private static String getStrValue(EntityKeyValue ekv) { | ||
389 | + switch (ekv.getDataType()) { | ||
390 | + case LONG: | ||
391 | + return ekv.getLngValue() != null ? ekv.getLngValue().toString() : null; | ||
392 | + case DOUBLE: | ||
393 | + return ekv.getDblValue() != null ? ekv.getDblValue().toString() : null; | ||
394 | + case BOOLEAN: | ||
395 | + return ekv.getBoolValue() != null ? ekv.getBoolValue().toString() : null; | ||
396 | + case STRING: | ||
397 | + return ekv.getStrValue(); | ||
398 | + case JSON: | ||
399 | + return ekv.getJsonValue(); | ||
400 | + default: | ||
401 | + return null; | ||
402 | + } | ||
403 | + } | ||
404 | + | ||
405 | + private static Double getDblValue(EntityKeyValue ekv) { | ||
406 | + switch (ekv.getDataType()) { | ||
407 | + case LONG: | ||
408 | + return ekv.getLngValue() != null ? ekv.getLngValue().doubleValue() : null; | ||
409 | + case DOUBLE: | ||
410 | + return ekv.getDblValue() != null ? ekv.getDblValue() : null; | ||
411 | + case BOOLEAN: | ||
412 | + return ekv.getBoolValue() != null ? (ekv.getBoolValue() ? 1.0 : 0.0) : null; | ||
413 | + case STRING: | ||
414 | + try { | ||
415 | + return Double.parseDouble(ekv.getStrValue()); | ||
416 | + } catch (RuntimeException e) { | ||
417 | + return null; | ||
418 | + } | ||
419 | + case JSON: | ||
420 | + try { | ||
421 | + return Double.parseDouble(ekv.getJsonValue()); | ||
422 | + } catch (RuntimeException e) { | ||
423 | + return null; | ||
424 | + } | ||
425 | + default: | ||
426 | + return null; | ||
427 | + } | ||
428 | + } | ||
429 | + | ||
430 | + private static Boolean getBoolValue(EntityKeyValue ekv) { | ||
431 | + switch (ekv.getDataType()) { | ||
432 | + case LONG: | ||
433 | + return ekv.getLngValue() != null ? ekv.getLngValue() > 0 : null; | ||
434 | + case DOUBLE: | ||
435 | + return ekv.getDblValue() != null ? ekv.getDblValue() > 0 : null; | ||
436 | + case BOOLEAN: | ||
437 | + return ekv.getBoolValue(); | ||
438 | + case STRING: | ||
439 | + try { | ||
440 | + return Boolean.parseBoolean(ekv.getStrValue()); | ||
441 | + } catch (RuntimeException e) { | ||
442 | + return null; | ||
443 | + } | ||
444 | + case JSON: | ||
445 | + try { | ||
446 | + return Boolean.parseBoolean(ekv.getJsonValue()); | ||
447 | + } catch (RuntimeException e) { | ||
448 | + return null; | ||
449 | + } | ||
450 | + default: | ||
451 | + return null; | ||
452 | + } | ||
453 | + } | ||
454 | + | ||
412 | } | 455 | } |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -98,6 +98,10 @@ class AlarmState { | @@ -98,6 +98,10 @@ class AlarmState { | ||
98 | Boolean evalResult = evalFunction.apply(clearState, data); | 98 | Boolean evalResult = evalFunction.apply(clearState, data); |
99 | if (evalResult) { | 99 | if (evalResult) { |
100 | stateUpdate |= clearState.checkUpdate(); | 100 | stateUpdate |= clearState.checkUpdate(); |
101 | + for (AlarmRuleState state : createRulesSortedBySeverityDesc) { | ||
102 | + state.clear(); | ||
103 | + stateUpdate |= state.checkUpdate(); | ||
104 | + } | ||
101 | ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); | 105 | ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); |
102 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); | 106 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); |
103 | currentAlarm = null; | 107 | currentAlarm = null; |
@@ -175,6 +179,8 @@ class AlarmState { | @@ -175,6 +179,8 @@ class AlarmState { | ||
175 | 179 | ||
176 | private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) { | 180 | private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) { |
177 | if (currentAlarm != null) { | 181 | if (currentAlarm != null) { |
182 | + // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). | ||
183 | + // Maybe we should fetch alarm every time? | ||
178 | currentAlarm.setEndTs(System.currentTimeMillis()); | 184 | currentAlarm.setEndTs(System.currentTimeMillis()); |
179 | AlarmSeverity oldSeverity = currentAlarm.getSeverity(); | 185 | AlarmSeverity oldSeverity = currentAlarm.getSeverity(); |
180 | if (!oldSeverity.equals(severity)) { | 186 | if (!oldSeverity.equals(severity)) { |
@@ -22,8 +22,16 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity; | @@ -22,8 +22,16 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity; | ||
22 | import org.thingsboard.server.common.data.device.profile.AlarmRule; | 22 | import org.thingsboard.server.common.data.device.profile.AlarmRule; |
23 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | 23 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
24 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 24 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
25 | +import org.thingsboard.server.common.data.query.ComplexFilterPredicate; | ||
26 | +import org.thingsboard.server.common.data.query.DynamicValue; | ||
27 | +import org.thingsboard.server.common.data.query.DynamicValueSourceType; | ||
25 | import org.thingsboard.server.common.data.query.EntityKey; | 28 | import org.thingsboard.server.common.data.query.EntityKey; |
29 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
30 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | ||
26 | import org.thingsboard.server.common.data.query.KeyFilter; | 31 | import org.thingsboard.server.common.data.query.KeyFilter; |
32 | +import org.thingsboard.server.common.data.query.KeyFilterPredicate; | ||
33 | +import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate; | ||
34 | +import org.thingsboard.server.common.data.query.StringFilterPredicate; | ||
27 | 35 | ||
28 | import javax.print.attribute.standard.Severity; | 36 | import javax.print.attribute.standard.Severity; |
29 | import java.util.Collections; | 37 | import java.util.Collections; |
@@ -65,6 +73,7 @@ class ProfileState { | @@ -65,6 +73,7 @@ class ProfileState { | ||
65 | for (KeyFilter keyFilter : alarmRule.getCondition().getCondition()) { | 73 | for (KeyFilter keyFilter : alarmRule.getCondition().getCondition()) { |
66 | entityKeys.add(keyFilter.getKey()); | 74 | entityKeys.add(keyFilter.getKey()); |
67 | ruleKeys.add(keyFilter.getKey()); | 75 | ruleKeys.add(keyFilter.getKey()); |
76 | + addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, ruleKeys); | ||
68 | } | 77 | } |
69 | })); | 78 | })); |
70 | if (alarm.getClearRule() != null) { | 79 | if (alarm.getClearRule() != null) { |
@@ -72,12 +81,33 @@ class ProfileState { | @@ -72,12 +81,33 @@ class ProfileState { | ||
72 | for (KeyFilter keyFilter : alarm.getClearRule().getCondition().getCondition()) { | 81 | for (KeyFilter keyFilter : alarm.getClearRule().getCondition().getCondition()) { |
73 | entityKeys.add(keyFilter.getKey()); | 82 | entityKeys.add(keyFilter.getKey()); |
74 | clearAlarmKeys.add(keyFilter.getKey()); | 83 | clearAlarmKeys.add(keyFilter.getKey()); |
84 | + addDynamicValuesRecursively(keyFilter.getPredicate(), entityKeys, clearAlarmKeys); | ||
75 | } | 85 | } |
76 | } | 86 | } |
77 | } | 87 | } |
78 | } | 88 | } |
79 | } | 89 | } |
80 | 90 | ||
91 | + private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<EntityKey> entityKeys, Set<EntityKey> ruleKeys) { | ||
92 | + switch (predicate.getType()) { | ||
93 | + case STRING: | ||
94 | + case NUMERIC: | ||
95 | + case BOOLEAN: | ||
96 | + DynamicValue value = ((SimpleKeyFilterPredicate) predicate).getValue().getDynamicValue(); | ||
97 | + if (value != null && value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE) { | ||
98 | + EntityKey entityKey = new EntityKey(EntityKeyType.ATTRIBUTE, value.getSourceAttribute()); | ||
99 | + entityKeys.add(entityKey); | ||
100 | + ruleKeys.add(entityKey); | ||
101 | + } | ||
102 | + break; | ||
103 | + case COMPLEX: | ||
104 | + for (KeyFilterPredicate child : ((ComplexFilterPredicate) predicate).getPredicates()) { | ||
105 | + addDynamicValuesRecursively(child, entityKeys, ruleKeys); | ||
106 | + } | ||
107 | + break; | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
81 | DeviceProfileId getProfileId() { | 111 | DeviceProfileId getProfileId() { |
82 | return deviceProfile.getId(); | 112 | return deviceProfile.getId(); |
83 | } | 113 | } |
@@ -89,6 +89,10 @@ public class TbDeviceProfileNode implements TbNode { | @@ -89,6 +89,10 @@ public class TbDeviceProfileNode implements TbNode { | ||
89 | } | 89 | } |
90 | log.info("[{}] Fetched alarm rule state for {} entities", ctx.getSelfId(), fetchCount); | 90 | log.info("[{}] Fetched alarm rule state for {} entities", ctx.getSelfId(), fetchCount); |
91 | } | 91 | } |
92 | + if (!config.isPersistAlarmRulesState() && ctx.isLocalEntity(ctx.getSelfId())) { | ||
93 | + log.info("[{}] Going to cleanup rule node states", ctx.getSelfId()); | ||
94 | + ctx.clearRuleNodeStates(); | ||
95 | + } | ||
92 | } | 96 | } |
93 | 97 | ||
94 | /** | 98 | /** |