Showing
4 changed files
with
67 additions
and
27 deletions
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 |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -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 |