From 51ac96d0102bb299cf86bb1ccb272aadeb03dfa5 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 15 Oct 2020 19:32:02 +0300 Subject: [PATCH] Alarm Result State --- .../rule/engine/profile/AlarmEvalResult.java | 22 +++++++++++ .../rule/engine/profile/AlarmRuleState.java | 30 ++++++++------- .../rule/engine/profile/AlarmState.java | 38 +++++++++++++------ .../engine/profile/TbDeviceProfileNode.java | 4 +- 4 files changed, 67 insertions(+), 27 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmEvalResult.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmEvalResult.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmEvalResult.java new file mode 100644 index 0000000000..7d510fe5fe --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmEvalResult.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.profile; + +public enum AlarmEvalResult { + + FALSE, NOT_YET_TRUE, TRUE; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index d9d08efbdc..8be2b81a59 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -123,17 +123,17 @@ class AlarmRuleState { } } - public boolean eval(DataSnapshot data) { + public AlarmEvalResult eval(DataSnapshot data) { boolean active = isActive(data.getTs()); switch (spec.getType()) { case SIMPLE: - return active && eval(alarmRule.getCondition(), data); + return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; case DURATION: return evalDuration(data, active); case REPEATING: return evalRepeating(data, active); default: - return false; + return AlarmEvalResult.FALSE; } } @@ -203,17 +203,17 @@ class AlarmRuleState { } } - private boolean evalRepeating(DataSnapshot data, boolean active) { + private AlarmEvalResult evalRepeating(DataSnapshot data, boolean active) { if (active && eval(alarmRule.getCondition(), data)) { state.setEventCount(state.getEventCount() + 1); updateFlag = true; - return state.getEventCount() >= requiredRepeats; + return state.getEventCount() >= requiredRepeats ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; } else { - return false; + return AlarmEvalResult.FALSE; } } - private boolean evalDuration(DataSnapshot data, boolean active) { + private AlarmEvalResult evalDuration(DataSnapshot data, boolean active) { if (active && eval(alarmRule.getCondition(), data)) { if (state.getLastEventTs() > 0) { if (data.getTs() > state.getLastEventTs()) { @@ -226,24 +226,28 @@ class AlarmRuleState { state.setDuration(0L); updateFlag = true; } - return state.getDuration() > requiredDurationInMs; + return state.getDuration() > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; } else { - return false; + return AlarmEvalResult.FALSE; } } - public boolean eval(long ts) { + public AlarmEvalResult eval(long ts) { switch (spec.getType()) { case SIMPLE: case REPEATING: - return false; + return AlarmEvalResult.NOT_YET_TRUE; case DURATION: if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { long duration = state.getDuration() + (ts - state.getLastEventTs()); - return duration > requiredDurationInMs && isActive(ts); + if (isActive(ts)) { + return duration > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; + } else { + return AlarmEvalResult.FALSE; + } } default: - return false; + return AlarmEvalResult.FALSE; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java index 5fb2c2957c..df46a4bf88 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java @@ -72,7 +72,7 @@ class AlarmState { return createOrClearAlarms(ctx, ts, null, AlarmRuleState::eval); } - public boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction evalFunction) { + public boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction evalFunction) { boolean stateUpdate = false; AlarmSeverity resultSeverity = null; log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); @@ -81,22 +81,28 @@ class AlarmState { log.debug("[{}][{}] Update is not valid for current rule state", alarmDefinition.getId(), state.getSeverity()); continue; } - boolean evalResult = evalFunction.apply(state, data); + AlarmEvalResult evalResult = evalFunction.apply(state, data); stateUpdate |= state.checkUpdate(); - if (evalResult) { + if (AlarmEvalResult.TRUE.equals(evalResult)) { resultSeverity = state.getSeverity(); break; + } else if (AlarmEvalResult.FALSE.equals(evalResult)) { + state.clear(); + stateUpdate |= state.checkUpdate(); } } if (resultSeverity != null) { - pushMsg(ctx, calculateAlarmResult(ctx, resultSeverity)); + TbAlarmResult result = calculateAlarmResult(ctx, resultSeverity); + if (result != null) { + pushMsg(ctx, result); + } } else if (currentAlarm != null && clearState != null) { if (!validateUpdate(update, clearState)) { log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); return stateUpdate; } - Boolean evalResult = evalFunction.apply(clearState, data); - if (evalResult) { + AlarmEvalResult evalResult = evalFunction.apply(clearState, data); + if (AlarmEvalResult.TRUE.equals(evalResult)) { stateUpdate |= clearState.checkUpdate(); for (AlarmRuleState state : createRulesSortedBySeverityDesc) { state.clear(); @@ -105,6 +111,9 @@ class AlarmState { ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); currentAlarm = null; + } else if (AlarmEvalResult.FALSE.equals(evalResult)) { + clearState.clear(); + stateUpdate |= clearState.checkUpdate(); } } return stateUpdate; @@ -183,13 +192,18 @@ class AlarmState { // Maybe we should fetch alarm every time? currentAlarm.setEndTs(System.currentTimeMillis()); AlarmSeverity oldSeverity = currentAlarm.getSeverity(); - if (!oldSeverity.equals(severity)) { - currentAlarm.setSeverity(severity); - currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); - return new TbAlarmResult(false, false, true, false, currentAlarm); + // Skip update if severity is decreased. + if (severity.ordinal() <= oldSeverity.ordinal()) { + if (!oldSeverity.equals(severity)) { + currentAlarm.setSeverity(severity); + currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); + return new TbAlarmResult(false, false, true, false, currentAlarm); + } else { + currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); + return new TbAlarmResult(false, true, false, false, currentAlarm); + } } else { - currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); - return new TbAlarmResult(false, true, false, false, currentAlarm); + return null; } } else { currentAlarm = new Alarm(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index 3fff9dbc2e..b4bbf08323 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -167,14 +167,14 @@ public class TbDeviceProfileNode implements TbNode { protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) throws ExecutionException, InterruptedException { DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceProfileId); if (deviceProfile != null) { - log.info("[{}] Received device profile update notification: {}", ctx.getSelfId(), deviceProfile); + log.debug("[{}] Received device profile update notification: {}", ctx.getSelfId(), deviceProfile); for (DeviceState state : deviceStates.values()) { if (deviceProfile.getId().equals(state.getProfileId())) { state.updateProfile(ctx, deviceProfile); } } } else { - log.info("[{}] Received stale profile update notification: [{}]", ctx.getSelfId(), deviceProfileId); + log.debug("[{}] Received stale profile update notification: [{}]", ctx.getSelfId(), deviceProfileId); } }