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 | 123 | } |
124 | 124 | } |
125 | 125 | |
126 | - public boolean eval(DataSnapshot data) { | |
126 | + public AlarmEvalResult eval(DataSnapshot data) { | |
127 | 127 | boolean active = isActive(data.getTs()); |
128 | 128 | switch (spec.getType()) { |
129 | 129 | case SIMPLE: |
130 | - return active && eval(alarmRule.getCondition(), data); | |
130 | + return (active && eval(alarmRule.getCondition(), data)) ? AlarmEvalResult.TRUE : AlarmEvalResult.FALSE; | |
131 | 131 | case DURATION: |
132 | 132 | return evalDuration(data, active); |
133 | 133 | case REPEATING: |
134 | 134 | return evalRepeating(data, active); |
135 | 135 | default: |
136 | - return false; | |
136 | + return AlarmEvalResult.FALSE; | |
137 | 137 | } |
138 | 138 | } |
139 | 139 | |
... | ... | @@ -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 | 207 | if (active && eval(alarmRule.getCondition(), data)) { |
208 | 208 | state.setEventCount(state.getEventCount() + 1); |
209 | 209 | updateFlag = true; |
210 | - return state.getEventCount() >= requiredRepeats; | |
210 | + return state.getEventCount() >= requiredRepeats ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; | |
211 | 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 | 217 | if (active && eval(alarmRule.getCondition(), data)) { |
218 | 218 | if (state.getLastEventTs() > 0) { |
219 | 219 | if (data.getTs() > state.getLastEventTs()) { |
... | ... | @@ -226,24 +226,28 @@ class AlarmRuleState { |
226 | 226 | state.setDuration(0L); |
227 | 227 | updateFlag = true; |
228 | 228 | } |
229 | - return state.getDuration() > requiredDurationInMs; | |
229 | + return state.getDuration() > requiredDurationInMs ? AlarmEvalResult.TRUE : AlarmEvalResult.NOT_YET_TRUE; | |
230 | 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 | 236 | switch (spec.getType()) { |
237 | 237 | case SIMPLE: |
238 | 238 | case REPEATING: |
239 | - return false; | |
239 | + return AlarmEvalResult.NOT_YET_TRUE; | |
240 | 240 | case DURATION: |
241 | 241 | if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) { |
242 | 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 | 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 | 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 | 76 | boolean stateUpdate = false; |
77 | 77 | AlarmSeverity resultSeverity = null; |
78 | 78 | log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); |
... | ... | @@ -81,22 +81,28 @@ class AlarmState { |
81 | 81 | log.debug("[{}][{}] Update is not valid for current rule state", alarmDefinition.getId(), state.getSeverity()); |
82 | 82 | continue; |
83 | 83 | } |
84 | - boolean evalResult = evalFunction.apply(state, data); | |
84 | + AlarmEvalResult evalResult = evalFunction.apply(state, data); | |
85 | 85 | stateUpdate |= state.checkUpdate(); |
86 | - if (evalResult) { | |
86 | + if (AlarmEvalResult.TRUE.equals(evalResult)) { | |
87 | 87 | resultSeverity = state.getSeverity(); |
88 | 88 | break; |
89 | + } else if (AlarmEvalResult.FALSE.equals(evalResult)) { | |
90 | + state.clear(); | |
91 | + stateUpdate |= state.checkUpdate(); | |
89 | 92 | } |
90 | 93 | } |
91 | 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 | 99 | } else if (currentAlarm != null && clearState != null) { |
94 | 100 | if (!validateUpdate(update, clearState)) { |
95 | 101 | log.debug("[{}] Update is not valid for current clear state", alarmDefinition.getId()); |
96 | 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 | 106 | stateUpdate |= clearState.checkUpdate(); |
101 | 107 | for (AlarmRuleState state : createRulesSortedBySeverityDesc) { |
102 | 108 | state.clear(); |
... | ... | @@ -105,6 +111,9 @@ class AlarmState { |
105 | 111 | ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); |
106 | 112 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm)); |
107 | 113 | currentAlarm = null; |
114 | + } else if (AlarmEvalResult.FALSE.equals(evalResult)) { | |
115 | + clearState.clear(); | |
116 | + stateUpdate |= clearState.checkUpdate(); | |
108 | 117 | } |
109 | 118 | } |
110 | 119 | return stateUpdate; |
... | ... | @@ -183,13 +192,18 @@ class AlarmState { |
183 | 192 | // Maybe we should fetch alarm every time? |
184 | 193 | currentAlarm.setEndTs(System.currentTimeMillis()); |
185 | 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 | 205 | } else { |
191 | - currentAlarm = ctx.getAlarmService().createOrUpdateAlarm(currentAlarm); | |
192 | - return new TbAlarmResult(false, true, false, false, currentAlarm); | |
206 | + return null; | |
193 | 207 | } |
194 | 208 | } else { |
195 | 209 | currentAlarm = new Alarm(); | ... | ... |
... | ... | @@ -167,14 +167,14 @@ public class TbDeviceProfileNode implements TbNode { |
167 | 167 | protected void updateProfile(TbContext ctx, DeviceProfileId deviceProfileId) throws ExecutionException, InterruptedException { |
168 | 168 | DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceProfileId); |
169 | 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 | 171 | for (DeviceState state : deviceStates.values()) { |
172 | 172 | if (deviceProfile.getId().equals(state.getProfileId())) { |
173 | 173 | state.updateProfile(ctx, deviceProfile); |
174 | 174 | } |
175 | 175 | } |
176 | 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 | ... | ... |