Commit 6d185f1c01090c3180b7312f1fdfbd258f8ba8ba

Authored by Andrii Shvaika
1 parent c4625177

Improvements to device profile rule node

@@ -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 }
@@ -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 /**