Commit 51ac96d0102bb299cf86bb1ccb272aadeb03dfa5

Authored by Andrii Shvaika
1 parent 7a34e2a3

Alarm Result State

  1 +/**
  2 + * Copyright © 2016-2020 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.rule.engine.profile;
  17 +
  18 +public enum AlarmEvalResult {
  19 +
  20 + FALSE, NOT_YET_TRUE, TRUE;
  21 +
  22 +}
@@ -123,17 +123,17 @@ class AlarmRuleState { @@ -123,17 +123,17 @@ class AlarmRuleState {
123 } 123 }
124 } 124 }
125 125
126 - public boolean eval(DataSnapshot data) { 126 + public AlarmEvalResult eval(DataSnapshot data) {
127 boolean active = isActive(data.getTs()); 127 boolean active = isActive(data.getTs());
128 switch (spec.getType()) { 128 switch (spec.getType()) {
129 case SIMPLE: 129 case SIMPLE:
130 - return active && eval(alarmRule.getCondition(), data); 130 + return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE;
131 case DURATION: 131 case DURATION:
132 return evalDuration(data, active); 132 return evalDuration(data, active);
133 case REPEATING: 133 case REPEATING:
134 return evalRepeating(data, active); 134 return evalRepeating(data, active);
135 default: 135 default:
136 - return false; 136 + return AlarmEvalResult.FALSE;
137 } 137 }
138 } 138 }
139 139
@@ -203,17 +203,17 @@ class AlarmRuleState { @@ -203,17 +203,17 @@ class AlarmRuleState {
203 } 203 }
204 } 204 }
205 205
206 - private boolean evalRepeating(DataSnapshot data, boolean active) { 206 + private AlarmEvalResult evalRepeating(DataSnapshot data, boolean active) {
207 if (active && eval(alarmRule.getCondition(), data)) { 207 if (active && eval(alarmRule.getCondition(), data)) {
208 state.setEventCount(state.getEventCount() + 1); 208 state.setEventCount(state.getEventCount() + 1);
209 updateFlag = true; 209 updateFlag = true;
210 - return state.getEventCount() >= requiredRepeats; 210 + return state.getEventCount() >= requiredRepeats ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE;
211 } else { 211 } else {
212 - return false; 212 + return AlarmEvalResult.FALSE;
213 } 213 }
214 } 214 }
215 215
216 - private boolean evalDuration(DataSnapshot data, boolean active) { 216 + private AlarmEvalResult evalDuration(DataSnapshot data, boolean active) {
217 if (active && eval(alarmRule.getCondition(), data)) { 217 if (active && eval(alarmRule.getCondition(), data)) {
218 if (state.getLastEventTs() > 0) { 218 if (state.getLastEventTs() > 0) {
219 if (data.getTs() > state.getLastEventTs()) { 219 if (data.getTs() > state.getLastEventTs()) {
@@ -226,24 +226,28 @@ class AlarmRuleState { @@ -226,24 +226,28 @@ class AlarmRuleState {
226 state.setDuration(0L); 226 state.setDuration(0L);
227 updateFlag = true; 227 updateFlag = true;
228 } 228 }
229 - return state.getDuration() > requiredDurationInMs; 229 + return state.getDuration() > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE;
230 } else { 230 } else {
231 - return false; 231 + return AlarmEvalResult.FALSE;
232 } 232 }
233 } 233 }
234 234
235 - public boolean eval(long ts) { 235 + public AlarmEvalResult eval(long ts) {
236 switch (spec.getType()) { 236 switch (spec.getType()) {
237 case SIMPLE: 237 case SIMPLE:
238 case REPEATING: 238 case REPEATING:
239 - return false; 239 + return AlarmEvalResult.NOT_YET_TRUE;
240 case DURATION: 240 case DURATION:
241 if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { 241 if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) {
242 long duration = state.getDuration() + (ts - state.getLastEventTs()); 242 long duration = state.getDuration() + (ts - state.getLastEventTs());
243 - return duration > requiredDurationInMs && isActive(ts); 243 + if (isActive(ts)) {
  244 + return duration > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE;
  245 + } else {
  246 + return AlarmEvalResult.FALSE;
  247 + }
244 } 248 }
245 default: 249 default:
246 - return false; 250 + return AlarmEvalResult.FALSE;
247 } 251 }
248 } 252 }
249 253
@@ -72,7 +72,7 @@ class AlarmState { @@ -72,7 +72,7 @@ class AlarmState {
72 return createOrClearAlarms(ctx, ts, null, AlarmRuleState::eval); 72 return createOrClearAlarms(ctx, ts, null, AlarmRuleState::eval);
73 } 73 }
74 74
75 - public <T> boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, Boolean> evalFunction) { 75 + public <T> boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction<AlarmRuleState, T, AlarmEvalResult> evalFunction) {
76 boolean stateUpdate = false; 76 boolean stateUpdate = false;
77 AlarmSeverity resultSeverity = null; 77 AlarmSeverity resultSeverity = null;
78 log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); 78 log.debug("[{}] processing update: {}", alarmDefinition.getId(), data);
@@ -81,22 +81,28 @@ class AlarmState { @@ -81,22 +81,28 @@ class AlarmState {
81 log.debug("[{}][{}] Update is not valid for current rule state", alarmDefinition.getId(), state.getSeverity()); 81 log.debug("[{}][{}] Update is not valid for current rule state", alarmDefinition.getId(), state.getSeverity());
82 continue; 82 continue;
83 } 83 }
84 - boolean evalResult = evalFunction.apply(state, data); 84 + AlarmEvalResult evalResult = evalFunction.apply(state, data);
85 stateUpdate |= state.checkUpdate(); 85 stateUpdate |= state.checkUpdate();
86 - if (evalResult) { 86 + if (AlarmEvalResult.TRUE.equals(evalResult)) {
87 resultSeverity = state.getSeverity(); 87 resultSeverity = state.getSeverity();
88 break; 88 break;
  89 + } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
  90 + state.clear();
  91 + stateUpdate |= state.checkUpdate();
89 } 92 }
90 } 93 }
91 if (resultSeverity != null) { 94 if (resultSeverity != null) {
92 - pushMsg(ctx, calculateAlarmResult(ctx, resultSeverity)); 95 + TbAlarmResult result = calculateAlarmResult(ctx, resultSeverity);
  96 + if (result != null) {
  97 + pushMsg(ctx, result);
  98 + }
93 } else if (currentAlarm != null && clearState != null) { 99 } else if (currentAlarm != null && clearState != null) {
94 if (!validateUpdate(update, clearState)) { 100 if (!validateUpdate(update, clearState)) {
95 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); 101 log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId());
96 return stateUpdate; 102 return stateUpdate;
97 } 103 }
98 - Boolean evalResult = evalFunction.apply(clearState, data);  
99 - if (evalResult) { 104 + AlarmEvalResult evalResult = evalFunction.apply(clearState, data);
  105 + if (AlarmEvalResult.TRUE.equals(evalResult)) {
100 stateUpdate |= clearState.checkUpdate(); 106 stateUpdate |= clearState.checkUpdate();
101 for (AlarmRuleState state : createRulesSortedBySeverityDesc) { 107 for (AlarmRuleState state : createRulesSortedBySeverityDesc) {
102 state.clear(); 108 state.clear();
@@ -105,6 +111,9 @@ class AlarmState { @@ -105,6 +111,9 @@ class AlarmState {
105 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); 111 ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis());
106 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); 112 pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm));
107 currentAlarm = null; 113 currentAlarm = null;
  114 + } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
  115 + clearState.clear();
  116 + stateUpdate |= clearState.checkUpdate();
108 } 117 }
109 } 118 }
110 return stateUpdate; 119 return stateUpdate;
@@ -183,13 +192,18 @@ class AlarmState { @@ -183,13 +192,18 @@ class AlarmState {
183 // Maybe we should fetch alarm every time? 192 // Maybe we should fetch alarm every time?
184 currentAlarm.setEndTs(System.currentTimeMillis()); 193 currentAlarm.setEndTs(System.currentTimeMillis());
185 AlarmSeverity oldSeverity = currentAlarm.getSeverity(); 194 AlarmSeverity oldSeverity = currentAlarm.getSeverity();
186 - if (!oldSeverity.equals(severity)) {  
187 - currentAlarm.setSeverity(severity);  
188 - currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm);  
189 - return new TbAlarmResult(false, false, true, false, currentAlarm); 195 + // Skip update if severity is decreased.
  196 + if (severity.ordinal() <= oldSeverity.ordinal()) {
  197 + if (!oldSeverity.equals(severity)) {
  198 + currentAlarm.setSeverity(severity);
  199 + currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm);
  200 + return new TbAlarmResult(false, false, true, false, currentAlarm);
  201 + } else {
  202 + currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm);
  203 + return new TbAlarmResult(false, true, false, false, currentAlarm);
  204 + }
190 } else { 205 } else {
191 - currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm);  
192 - return new TbAlarmResult(false, true, false, false, currentAlarm); 206 + return null;
193 } 207 }
194 } else { 208 } else {
195 currentAlarm = new Alarm(); 209 currentAlarm = new Alarm();
@@ -167,14 +167,14 @@ public class TbDeviceProfileNode implements TbNode { @@ -167,14 +167,14 @@ public class TbDeviceProfileNode implements TbNode {
167 protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) throws ExecutionException, InterruptedException { 167 protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) throws ExecutionException, InterruptedException {
168 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceProfileId); 168 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceProfileId);
169 if (deviceProfile != null) { 169 if (deviceProfile != null) {
170 - log.info("[{}] Received device profile update notification: {}", ctx.getSelfId(), deviceProfile); 170 + log.debug("[{}] Received device profile update notification: {}", ctx.getSelfId(), deviceProfile);
171 for (DeviceState state : deviceStates.values()) { 171 for (DeviceState state : deviceStates.values()) {
172 if (deviceProfile.getId().equals(state.getProfileId())) { 172 if (deviceProfile.getId().equals(state.getProfileId())) {
173 state.updateProfile(ctx, deviceProfile); 173 state.updateProfile(ctx, deviceProfile);
174 } 174 }
175 } 175 }
176 } else { 176 } else {
177 - log.info("[{}] Received stale profile update notification: [{}]", ctx.getSelfId(), deviceProfileId); 177 + log.debug("[{}] Received stale profile update notification: [{}]", ctx.getSelfId(), deviceProfileId);
178 } 178 }
179 } 179 }
180 180