Showing
4 changed files
with
148 additions
and
93 deletions
@@ -175,4 +175,17 @@ public interface FastIotConstants { | @@ -175,4 +175,17 @@ public interface FastIotConstants { | ||
175 | /**RPC单项双向*/ | 175 | /**RPC单项双向*/ |
176 | public static String ONEWAY = "oneway"; | 176 | public static String ONEWAY = "oneway"; |
177 | } | 177 | } |
178 | + class Alarm{ | ||
179 | + | ||
180 | + /**遥测指标标识符*/ | ||
181 | + public static String KEY = "key"; | ||
182 | + /**遥测指标值*/ | ||
183 | + public static String VALUE = "value"; | ||
184 | + /**触发器逻辑关系*/ | ||
185 | + public static String PREDICATE = "logic"; | ||
186 | + /**触发器*/ | ||
187 | + public static String TRIGGER = "trigger"; | ||
188 | + /**执行条件*/ | ||
189 | + public static String CONDITION = "condition"; | ||
190 | + } | ||
178 | } | 191 | } |
@@ -89,24 +89,27 @@ class ReactState { | @@ -89,24 +89,27 @@ class ReactState { | ||
89 | * | 89 | * |
90 | * @param ctx | 90 | * @param ctx |
91 | * @param msg 设备推送的遥测数据 | 91 | * @param msg 设备推送的遥测数据 |
92 | + * @param prefixId 运算规则ID,例如:触发器、执行条件 | ||
92 | * @param deviceId 遥测数据的来源设备的TB设备ID | 93 | * @param deviceId 遥测数据的来源设备的TB设备ID |
93 | * @throws ExecutionException | 94 | * @throws ExecutionException |
94 | * @throws InterruptedException | 95 | * @throws InterruptedException |
95 | */ | 96 | */ |
96 | - public void process(TbContext ctx, TbMsg msg, String prefixId,String deviceId) | 97 | + public void process(TbContext ctx, TbMsg msg, String prefixId, String deviceId) |
97 | throws ExecutionException, InterruptedException { | 98 | throws ExecutionException, InterruptedException { |
98 | 99 | ||
99 | - StringBuilder detail = new StringBuilder(); | 100 | + /** 场景联动告警详情 */ |
101 | + ObjectNode detail = JacksonUtil.newObjectNode(); | ||
100 | if (actions == null) { | 102 | if (actions == null) { |
101 | ctx.tellSuccess(msg); | 103 | ctx.tellSuccess(msg); |
102 | } | 104 | } |
103 | 105 | ||
106 | + /** 1、单个触发器内只要有1个设备满足条件即为true 2、多个触发器只要有1个触发器满足条件即为true */ | ||
104 | AtomicBoolean triggerMatched = new AtomicBoolean(true); | 107 | AtomicBoolean triggerMatched = new AtomicBoolean(true); |
105 | Optional.ofNullable(triggers) | 108 | Optional.ofNullable(triggers) |
106 | .ifPresent( | 109 | .ifPresent( |
107 | - t -> { | 110 | + all -> { |
108 | triggerMatched.set(false); | 111 | triggerMatched.set(false); |
109 | - t.forEach( | 112 | + all.forEach( |
110 | trigger -> { | 113 | trigger -> { |
111 | ScopeEnum entityType = trigger.getEntityType(); | 114 | ScopeEnum entityType = trigger.getEntityType(); |
112 | List<String> trifggerDevices = trigger.getEntityId(); | 115 | List<String> trifggerDevices = trigger.getEntityId(); |
@@ -119,24 +122,30 @@ class ReactState { | @@ -119,24 +122,30 @@ class ReactState { | ||
119 | triggerMatched.set( | 122 | triggerMatched.set( |
120 | trifggerDevices.stream() | 123 | trifggerDevices.stream() |
121 | .anyMatch( | 124 | .anyMatch( |
122 | - id -> { | 125 | + devId -> { |
123 | TriggerState triggerState = | 126 | TriggerState triggerState = |
124 | - getOrCreateTriggerState(trigger, tkProjectId, id); | 127 | + getOrCreateTriggerState(trigger, tkProjectId, devId); |
125 | if (triggerState == null) { | 128 | if (triggerState == null) { |
126 | return false; | 129 | return false; |
127 | } | 130 | } |
128 | try { | 131 | try { |
129 | boolean fresh = false; | 132 | boolean fresh = false; |
130 | - if(trigger.getId().equals(prefixId)&&msg.getOriginator().getId().toString().equals(id)){ | ||
131 | - fresh=true; | 133 | + if (trigger.getId().equals(prefixId) |
134 | + && msg.getOriginator().getId().toString().equals(devId)) { | ||
135 | + fresh = true; | ||
132 | } | 136 | } |
133 | - boolean result = triggerState.process(ctx, msg,fresh); | ||
134 | - log.error(String.format("触发器【%s】刷新【%s】结果【%s】触发器设备【%s】数据设备【%s】数据内容【%s】",trigger.getId(),fresh,result,id,msg.getOriginator(),msg.getData())); | ||
135 | - if (result) { | ||
136 | - detail.append( | ||
137 | - triggerState.getAlarmDetails() == null | ||
138 | - ? "" | ||
139 | - : triggerState.getAlarmDetails()); | 137 | + ObjectNode result = triggerState.process(ctx, msg, fresh); |
138 | + log.error( | ||
139 | + String.format( | ||
140 | + "触发器【%s】刷新【%s】结果【%s】触发器设备【%s】数据设备【%s】数据内容【%s】", | ||
141 | + trigger.getId(), | ||
142 | + fresh, | ||
143 | + result, | ||
144 | + devId, | ||
145 | + msg.getOriginator(), | ||
146 | + msg.getData())); | ||
147 | + if (!result.isEmpty()) { | ||
148 | + detail.set(FastIotConstants.Alarm.TRIGGER,result); | ||
140 | return true; | 149 | return true; |
141 | } else if (currentAlarms.containsKey(deviceId)) { | 150 | } else if (currentAlarms.containsKey(deviceId)) { |
142 | // 清除设备告警 | 151 | // 清除设备告警 |
@@ -155,51 +164,65 @@ class ReactState { | @@ -155,51 +164,65 @@ class ReactState { | ||
155 | }); | 164 | }); |
156 | }); | 165 | }); |
157 | 166 | ||
158 | - /** 执行条件的所有设备都满足才为true */ | 167 | + /** 1、单个执行条件内全部设备满足条件才为true 2、多个执行条件全部执行条件满足条件才为true */ |
159 | AtomicBoolean conditionMatched = new AtomicBoolean(true); | 168 | AtomicBoolean conditionMatched = new AtomicBoolean(true); |
160 | Optional.ofNullable(conditions) | 169 | Optional.ofNullable(conditions) |
161 | - .ifPresent( | ||
162 | - t -> { | ||
163 | - t.forEach( | ||
164 | - condition -> { | ||
165 | - ScopeEnum entityType = condition.getEntityType(); | ||
166 | - List<String> conditionDevices = condition.getEntityId(); | ||
167 | - String tkProjectId = condition.getDeviceProfileId(); | ||
168 | - if (ScopeEnum.ALL.equals(entityType)) { | ||
169 | - conditionDevices = | ||
170 | - ytDeviceService.findTbDeviceIdsByDeviceProfileId( | ||
171 | - tkProjectId, condition.getTenantId()); | ||
172 | - } | ||
173 | - conditionMatched.set( | ||
174 | - !conditionDevices.stream() | ||
175 | - .anyMatch( | ||
176 | - id -> { | ||
177 | - TriggerState conditionState = | ||
178 | - getOrCreateConditionState(condition, tkProjectId, id); | ||
179 | - try { | ||
180 | - boolean fresh = false; | ||
181 | - if(msg.getOriginator().getId().toString().equals(id)){ | ||
182 | - fresh=true; | ||
183 | - } | ||
184 | - boolean result = conditionState.process(ctx, msg,fresh); | ||
185 | - log.warn(String.format("执行器【%s】刷新【%s】结果【%s】执行器设备【%s】数据设备【%s】数据内容【%s】",condition.getId(),fresh,result,id,msg.getOriginator(),msg.getData())); | ||
186 | - return !result; | ||
187 | - } catch (ExecutionException e) { | ||
188 | - throw new RuntimeException(e); | ||
189 | - } catch (InterruptedException e) { | ||
190 | - throw new RuntimeException(e); | ||
191 | - } | ||
192 | - })); | ||
193 | - }); | ||
194 | - }); | 170 | + .ifPresent( |
171 | + all -> { | ||
172 | + conditionMatched.set( | ||
173 | + all.stream() | ||
174 | + .allMatch( | ||
175 | + condition -> { | ||
176 | + ScopeEnum entityType = condition.getEntityType(); | ||
177 | + List<String> conditionDevices = condition.getEntityId(); | ||
178 | + String tkProjectId = condition.getDeviceProfileId(); | ||
179 | + if (ScopeEnum.ALL.equals(entityType)) { | ||
180 | + conditionDevices = | ||
181 | + ytDeviceService.findTbDeviceIdsByDeviceProfileId( | ||
182 | + tkProjectId, condition.getTenantId()); | ||
183 | + } | ||
184 | + | ||
185 | + return conditionDevices.stream() | ||
186 | + .allMatch( | ||
187 | + devId -> { | ||
188 | + TriggerState conditionState = | ||
189 | + getOrCreateConditionState(condition, tkProjectId, devId); | ||
190 | + try { | ||
191 | + boolean fresh = false; | ||
192 | + if (msg.getOriginator().getId().toString().equals(devId)) { | ||
193 | + fresh = true; | ||
194 | + } | ||
195 | + ObjectNode result = conditionState.process(ctx, msg, fresh); | ||
196 | + log.warn( | ||
197 | + String.format( | ||
198 | + "执行器【%s】刷新【%s】结果【%s】执行器设备【%s】数据设备【%s】数据内容【%s】", | ||
199 | + condition.getId(), | ||
200 | + fresh, | ||
201 | + result, | ||
202 | + devId, | ||
203 | + msg.getOriginator(), | ||
204 | + msg.getData())); | ||
205 | + if (!result.isEmpty()) { | ||
206 | + detail.set(FastIotConstants.Alarm.CONDITION,result); | ||
207 | + return true; | ||
208 | + } | ||
209 | + } catch (ExecutionException e) { | ||
210 | + throw new RuntimeException(e); | ||
211 | + } catch (InterruptedException e) { | ||
212 | + throw new RuntimeException(e); | ||
213 | + } | ||
214 | + return false; | ||
215 | + }); | ||
216 | + })); | ||
217 | + }); | ||
195 | 218 | ||
196 | if (triggerMatched.get() && conditionMatched.get()) { | 219 | if (triggerMatched.get() && conditionMatched.get()) { |
197 | - log.error(String.format("设备【%s】的消息内容【%s】触发动作",deviceId,msg.getData())); | 220 | + log.error(String.format("设备【%s】的消息内容【%s】触发动作", deviceId, msg.getData())); |
198 | for (TkDoActionEntity item : actions) { | 221 | for (TkDoActionEntity item : actions) { |
199 | if (ActionTypeEnum.MSG_NOTIFY.equals(item.getOutTarget())) { | 222 | if (ActionTypeEnum.MSG_NOTIFY.equals(item.getOutTarget())) { |
200 | - noticeMsg(ctx, msg, item, deviceId, detail.toString(), msg.getTs()); | 223 | + noticeMsg(ctx, msg, item, deviceId, detail, msg.getTs()); |
201 | } else { | 224 | } else { |
202 | - pushMsg(ctx, msg, item, detail.toString()); | 225 | + pushMsg(ctx, msg, item); |
203 | } | 226 | } |
204 | } | 227 | } |
205 | } else { | 228 | } else { |
@@ -226,7 +249,7 @@ class ReactState { | @@ -226,7 +249,7 @@ class ReactState { | ||
226 | || (trigger.getEntityType().equals(ScopeEnum.ALL) | 249 | || (trigger.getEntityType().equals(ScopeEnum.ALL) |
227 | && trigger.getDeviceProfileId().equals(profileId))) { | 250 | && trigger.getDeviceProfileId().equals(profileId))) { |
228 | TriggerState state = createTriggerState(deviceId, trigger.getTriggerCondition()); | 251 | TriggerState state = createTriggerState(deviceId, trigger.getTriggerCondition()); |
229 | - log.error(String.format("新建设备【%s】的触发器",deviceId)); | 252 | + log.error(String.format("新建设备【%s】的触发器", deviceId)); |
230 | triggerState.put(cacheKey, state); | 253 | triggerState.put(cacheKey, state); |
231 | return state; | 254 | return state; |
232 | } | 255 | } |
@@ -290,12 +313,12 @@ class ReactState { | @@ -290,12 +313,12 @@ class ReactState { | ||
290 | for (AlarmConditionFilter filter : rule.getCondition().getCondition()) { | 313 | for (AlarmConditionFilter filter : rule.getCondition().getCondition()) { |
291 | filterKeys.add(filter.getKey()); | 314 | filterKeys.add(filter.getKey()); |
292 | } | 315 | } |
293 | - TriggerState state = new TriggerState(deviceId, rule, filterKeys, rule.getAlarmDetails(), null); | 316 | + TriggerState state = new TriggerState(deviceId, rule, filterKeys, null); |
294 | 317 | ||
295 | return state; | 318 | return state; |
296 | } | 319 | } |
297 | 320 | ||
298 | - private void pushMsg(TbContext ctx, TbMsg msg, TkDoActionEntity action, String detail) { | 321 | + private void pushMsg(TbContext ctx, TbMsg msg, TkDoActionEntity action) { |
299 | switch (action.getOutTarget()) { | 322 | switch (action.getOutTarget()) { |
300 | case DEVICE_OUT: | 323 | case DEVICE_OUT: |
301 | List<String> rpcDevices = action.getDeviceId(); | 324 | List<String> rpcDevices = action.getDeviceId(); |
@@ -354,7 +377,7 @@ class ReactState { | @@ -354,7 +377,7 @@ class ReactState { | ||
354 | TbMsg msg, | 377 | TbMsg msg, |
355 | TkDoActionEntity action, | 378 | TkDoActionEntity action, |
356 | String deviceId, | 379 | String deviceId, |
357 | - String detailStr, | 380 | + ObjectNode detail, |
358 | long startTs) { | 381 | long startTs) { |
359 | 382 | ||
360 | DeviceId entityId = new DeviceId(UUID.fromString(deviceId)); | 383 | DeviceId entityId = new DeviceId(UUID.fromString(deviceId)); |
@@ -370,12 +393,7 @@ class ReactState { | @@ -370,12 +393,7 @@ class ReactState { | ||
370 | } | 393 | } |
371 | currentAlarm.setStartTs(startTs); | 394 | currentAlarm.setStartTs(startTs); |
372 | currentAlarm.setEndTs(currentAlarm.getStartTs()); | 395 | currentAlarm.setEndTs(currentAlarm.getStartTs()); |
373 | - ObjectNode detailData = JacksonUtil.newObjectNode(); | ||
374 | - if (StringUtils.isNotEmpty(detailStr)) { | ||
375 | - detailData.put("msg", detailStr); | ||
376 | - } | ||
377 | - detailData.put("data", JacksonUtil.toJsonNode(msg.getData())); | ||
378 | - currentAlarm.setDetails(detailData); | 396 | + currentAlarm.setDetails(detail); |
379 | currentAlarm.setOriginator(entityId); | 397 | currentAlarm.setOriginator(entityId); |
380 | currentAlarm.setTenantId(ctx.getTenantId()); | 398 | currentAlarm.setTenantId(ctx.getTenantId()); |
381 | currentAlarm.setPropagate(false); | 399 | currentAlarm.setPropagate(false); |
@@ -393,7 +411,7 @@ class ReactState { | @@ -393,7 +411,7 @@ class ReactState { | ||
393 | 411 | ||
394 | AlarmInfoDTO formData = new AlarmInfoDTO(); | 412 | AlarmInfoDTO formData = new AlarmInfoDTO(); |
395 | formData.setDeviceName(msg.getMetaData().getData().get("deviceName")); | 413 | formData.setDeviceName(msg.getMetaData().getData().get("deviceName")); |
396 | - formData.setDetails(JacksonUtil.toString(detailData)); | 414 | + formData.setDetails(JacksonUtil.toString(detail)); |
397 | formData.setType(currentAlarm.getType()); | 415 | formData.setType(currentAlarm.getType()); |
398 | formData.setCreateTs(currentAlarm.getCreatedTime()); | 416 | formData.setCreateTs(currentAlarm.getCreatedTime()); |
399 | formData.setStartTs(currentAlarm.getStartTs()); | 417 | formData.setStartTs(currentAlarm.getStartTs()); |
@@ -408,16 +426,19 @@ class ReactState { | @@ -408,16 +426,19 @@ class ReactState { | ||
408 | private void clearAlarm(TbContext ctx, TbMsg msg, String deviceId, String key) | 426 | private void clearAlarm(TbContext ctx, TbMsg msg, String deviceId, String key) |
409 | throws ExecutionException, InterruptedException { | 427 | throws ExecutionException, InterruptedException { |
410 | TriggerState clearState = getOrCreateClearState(deviceId, key); | 428 | TriggerState clearState = getOrCreateClearState(deviceId, key); |
411 | - if (clearState != null && clearState.process(ctx, msg,true)) { | ||
412 | - ctx.getAlarmService() | ||
413 | - .clearAlarmForResult( | ||
414 | - ctx.getTenantId(), | ||
415 | - currentAlarms.get(deviceId).getId(), | ||
416 | - null, | ||
417 | - System.currentTimeMillis()); | ||
418 | - ytDeviceService.freshAlarmStatus(new DeviceId(UUID.fromString(deviceId)), 0); | ||
419 | - alarmMsg(ctx, msg, currentAlarms.get(deviceId), "Alarm Cleared"); | ||
420 | - currentAlarms.remove(deviceId); | 429 | + if(clearState !=null){ |
430 | + ObjectNode clearResult = clearState.process(ctx, msg, true); | ||
431 | + if(!clearResult.isEmpty()){ | ||
432 | + ctx.getAlarmService() | ||
433 | + .clearAlarmForResult( | ||
434 | + ctx.getTenantId(), | ||
435 | + currentAlarms.get(deviceId).getId(), | ||
436 | + clearResult, | ||
437 | + System.currentTimeMillis()); | ||
438 | + ytDeviceService.freshAlarmStatus(new DeviceId(UUID.fromString(deviceId)), 0); | ||
439 | + alarmMsg(ctx, msg, currentAlarms.get(deviceId), "Alarm Cleared"); | ||
440 | + currentAlarms.remove(deviceId); | ||
441 | + } | ||
421 | } | 442 | } |
422 | } | 443 | } |
423 | 444 |
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | */ | 13 | */ |
14 | package org.thingsboard.rule.engine.yunteng.scene; | 14 | package org.thingsboard.rule.engine.yunteng.scene; |
15 | 15 | ||
16 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
16 | import com.google.gson.JsonParser; | 17 | import com.google.gson.JsonParser; |
17 | import java.util.*; | 18 | import java.util.*; |
18 | import java.util.concurrent.ExecutionException; | 19 | import java.util.concurrent.ExecutionException; |
@@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.id.DeviceId; | @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.id.DeviceId; | ||
32 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 33 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
33 | import org.thingsboard.server.common.data.kv.KvEntry; | 34 | import org.thingsboard.server.common.data.kv.KvEntry; |
34 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 35 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
35 | import org.thingsboard.server.common.msg.TbMsg; | 37 | import org.thingsboard.server.common.msg.TbMsg; |
36 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 38 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
37 | import org.thingsboard.server.common.msg.session.SessionMsgType; | 39 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
@@ -50,18 +52,14 @@ class TriggerState { | @@ -50,18 +52,14 @@ class TriggerState { | ||
50 | private final DynamicPredicateValueCtx dynamicPredicateValueCtx; | 52 | private final DynamicPredicateValueCtx dynamicPredicateValueCtx; |
51 | private DataSnapshot latestValues; | 53 | private DataSnapshot latestValues; |
52 | 54 | ||
55 | + /**触发器的遥测指标*/ | ||
53 | private final Set<AlarmConditionFilterKey> entityKeys; | 56 | private final Set<AlarmConditionFilterKey> entityKeys; |
54 | - private final String alarmDetails; | ||
55 | 57 | ||
56 | - @Getter | ||
57 | - /** 触发条件执行结果 */ | ||
58 | - private boolean ruleMatched = false; | ||
59 | 58 | ||
60 | TriggerState( | 59 | TriggerState( |
61 | String originator, | 60 | String originator, |
62 | AlarmRule rule, | 61 | AlarmRule rule, |
63 | Set<AlarmConditionFilterKey> filterKeys, | 62 | Set<AlarmConditionFilterKey> filterKeys, |
64 | - String alarmDetails, | ||
65 | DynamicPredicateValueCtx dynamicPredicateValueCtx) { | 63 | DynamicPredicateValueCtx dynamicPredicateValueCtx) { |
66 | 64 | ||
67 | this.originator = originator; | 65 | this.originator = originator; |
@@ -70,12 +68,12 @@ class TriggerState { | @@ -70,12 +68,12 @@ class TriggerState { | ||
70 | new TriggerRuleState( | 68 | new TriggerRuleState( |
71 | rule.getCondition(), filterKeys, new PersistedAlarmRuleState(), rule.getSchedule()); | 69 | rule.getCondition(), filterKeys, new PersistedAlarmRuleState(), rule.getSchedule()); |
72 | this.entityKeys = filterKeys; | 70 | this.entityKeys = filterKeys; |
73 | - this.alarmDetails = alarmDetails; | ||
74 | } | 71 | } |
75 | 72 | ||
76 | - public boolean process(TbContext ctx, TbMsg msg,boolean needFresh) throws ExecutionException, InterruptedException { | 73 | + public ObjectNode process(TbContext ctx, TbMsg msg, boolean needFresh) |
74 | + throws ExecutionException, InterruptedException { | ||
77 | if (!needFresh) { | 75 | if (!needFresh) { |
78 | - return ruleMatched; | 76 | + return ruleState.getDetailInform(); |
79 | } | 77 | } |
80 | if (latestValues == null) { | 78 | if (latestValues == null) { |
81 | latestValues = fetchLatestValues(ctx, originator); | 79 | latestValues = fetchLatestValues(ctx, originator); |
@@ -89,35 +87,33 @@ class TriggerState { | @@ -89,35 +87,33 @@ class TriggerState { | ||
89 | } | 87 | } |
90 | 88 | ||
91 | if (update != null && update.hasUpdate()) { | 89 | if (update != null && update.hasUpdate()) { |
92 | - ruleMatched = createOrClearAlarms(ctx, msg, latestValues, update, TriggerRuleState::eval); | ||
93 | - return ruleMatched; | 90 | + return createOrClearAlarms(ctx, msg, latestValues, update, TriggerRuleState::eval); |
94 | } | 91 | } |
95 | - return false; | 92 | + return JacksonUtil.newObjectNode(); |
96 | } | 93 | } |
97 | 94 | ||
98 | - public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException { | 95 | + public ObjectNode process(TbContext ctx, long ts) throws ExecutionException, InterruptedException { |
99 | return createOrClearAlarms( | 96 | return createOrClearAlarms( |
100 | ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, latestValues)); | 97 | ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, latestValues)); |
101 | } | 98 | } |
102 | 99 | ||
103 | - public <T> boolean createOrClearAlarms( | 100 | + public <T> ObjectNode createOrClearAlarms( |
104 | TbContext ctx, | 101 | TbContext ctx, |
105 | TbMsg msg, | 102 | TbMsg msg, |
106 | T data, | 103 | T data, |
107 | SnapshotUpdate update, | 104 | SnapshotUpdate update, |
108 | BiFunction<TriggerRuleState, T, AlarmEvalResult> evalFunction) { | 105 | BiFunction<TriggerRuleState, T, AlarmEvalResult> evalFunction) { |
109 | - boolean stateUpdate = false; | ||
110 | if (!validateUpdate(update, ruleState)) { | 106 | if (!validateUpdate(update, ruleState)) { |
111 | - return false; | 107 | + return JacksonUtil.newObjectNode(); |
112 | } | 108 | } |
109 | +// ruleState.getCondition(). | ||
113 | AlarmEvalResult evalResult = evalFunction.apply(ruleState, data); | 110 | AlarmEvalResult evalResult = evalFunction.apply(ruleState, data); |
114 | if (AlarmEvalResult.TRUE.equals(evalResult)) { | 111 | if (AlarmEvalResult.TRUE.equals(evalResult)) { |
115 | - stateUpdate = true; | ||
116 | - clearAlarmState(stateUpdate, ruleState); | 112 | + clearAlarmState(true, ruleState); |
117 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { | 113 | } else if (AlarmEvalResult.FALSE.equals(evalResult)) { |
118 | - clearAlarmState(stateUpdate, ruleState); | 114 | + clearAlarmState(false, ruleState); |
119 | } | 115 | } |
120 | - return stateUpdate; | 116 | + return ruleState.getDetailInform(); |
121 | } | 117 | } |
122 | 118 | ||
123 | public boolean clearAlarmState(boolean stateUpdate, TriggerRuleState state) { | 119 | public boolean clearAlarmState(boolean stateUpdate, TriggerRuleState state) { |
@@ -128,6 +124,12 @@ class TriggerState { | @@ -128,6 +124,12 @@ class TriggerState { | ||
128 | return stateUpdate; | 124 | return stateUpdate; |
129 | } | 125 | } |
130 | 126 | ||
127 | + /** | ||
128 | + * 快照中有更新的遥测指标是否包含在触发器中 | ||
129 | + * @param update 遥测数据的快照 | ||
130 | + * @param state 触发器状态 | ||
131 | + * @return | ||
132 | + */ | ||
131 | public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState state) { | 133 | public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState state) { |
132 | if (update != null) { | 134 | if (update != null) { |
133 | // Check that the update type and that keys match. | 135 | // Check that the update type and that keys match. |
@@ -3,7 +3,9 @@ | @@ -3,7 +3,9 @@ | ||
3 | */ | 3 | */ |
4 | package org.thingsboard.rule.engine.yunteng.utils; | 4 | package org.thingsboard.rule.engine.yunteng.utils; |
5 | 5 | ||
6 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
6 | import lombok.Data; | 7 | import lombok.Data; |
8 | +import lombok.Getter; | ||
7 | import lombok.extern.slf4j.Slf4j; | 9 | import lombok.extern.slf4j.Slf4j; |
8 | import org.thingsboard.rule.engine.profile.AlarmEvalResult; | 10 | import org.thingsboard.rule.engine.profile.AlarmEvalResult; |
9 | import org.thingsboard.rule.engine.profile.DataSnapshot; | 11 | import org.thingsboard.rule.engine.profile.DataSnapshot; |
@@ -12,6 +14,8 @@ import org.thingsboard.rule.engine.profile.EntityKeyValue; | @@ -12,6 +14,8 @@ import org.thingsboard.rule.engine.profile.EntityKeyValue; | ||
12 | import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState; | 14 | import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState; |
13 | import org.thingsboard.server.common.data.device.profile.*; | 15 | import org.thingsboard.server.common.data.device.profile.*; |
14 | import org.thingsboard.server.common.data.query.*; | 16 | import org.thingsboard.server.common.data.query.*; |
17 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
18 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | ||
15 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; | 19 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; |
16 | 20 | ||
17 | import java.time.Instant; | 21 | import java.time.Instant; |
@@ -33,6 +37,8 @@ public class TriggerRuleState { | @@ -33,6 +37,8 @@ public class TriggerRuleState { | ||
33 | private final DynamicPredicateValueCtx dynamicPredicateValueCtx =null; | 37 | private final DynamicPredicateValueCtx dynamicPredicateValueCtx =null; |
34 | 38 | ||
35 | 39 | ||
40 | + /**告警触发详情*/ | ||
41 | + private ObjectNode detailInform = null; | ||
36 | 42 | ||
37 | 43 | ||
38 | 44 | ||
@@ -46,6 +52,7 @@ public class TriggerRuleState { | @@ -46,6 +52,7 @@ public class TriggerRuleState { | ||
46 | this.state = new PersistedAlarmRuleState(0L, 0L, 0L); | 52 | this.state = new PersistedAlarmRuleState(0L, 0L, 0L); |
47 | } | 53 | } |
48 | this.spec = getSpec(condition); | 54 | this.spec = getSpec(condition); |
55 | + this.detailInform = JacksonUtil.newObjectNode(); | ||
49 | } | 56 | } |
50 | 57 | ||
51 | 58 | ||
@@ -279,7 +286,11 @@ public class TriggerRuleState { | @@ -279,7 +286,11 @@ public class TriggerRuleState { | ||
279 | if (value == null) { | 286 | if (value == null) { |
280 | return false; | 287 | return false; |
281 | } | 288 | } |
289 | + detailInform.put(FastIotConstants.Alarm.KEY,filter.getKey().getKey()); | ||
282 | eval = eval && eval(data, value, filter.getPredicate(), filter); | 290 | eval = eval && eval(data, value, filter.getPredicate(), filter); |
291 | + if(!eval){ | ||
292 | + detailInform.removeAll(); | ||
293 | + } | ||
283 | } | 294 | } |
284 | return eval; | 295 | return eval; |
285 | } | 296 | } |
@@ -320,6 +331,8 @@ public class TriggerRuleState { | @@ -320,6 +331,8 @@ public class TriggerRuleState { | ||
320 | } | 331 | } |
321 | 332 | ||
322 | private boolean evalComplexPredicate(DataSnapshot data, EntityKeyValue ekv, ComplexFilterPredicate predicate, AlarmConditionFilter filter) { | 333 | private boolean evalComplexPredicate(DataSnapshot data, EntityKeyValue ekv, ComplexFilterPredicate predicate, AlarmConditionFilter filter) { |
334 | + detailInform.put(FastIotConstants.Alarm.VALUE,ekv.getDataType().toString()); | ||
335 | + detailInform.put(FastIotConstants.Alarm.PREDICATE,predicate.getOperation().toString()); | ||
323 | switch (predicate.getOperation()) { | 336 | switch (predicate.getOperation()) { |
324 | case OR: | 337 | case OR: |
325 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { | 338 | for (KeyFilterPredicate kfp : predicate.getPredicates()) { |
@@ -349,6 +362,8 @@ public class TriggerRuleState { | @@ -349,6 +362,8 @@ public class TriggerRuleState { | ||
349 | if (predicateValue == null) { | 362 | if (predicateValue == null) { |
350 | return false; | 363 | return false; |
351 | } | 364 | } |
365 | + detailInform.put(FastIotConstants.Alarm.VALUE,predicateValue); | ||
366 | + detailInform.put(FastIotConstants.Alarm.PREDICATE,predicate.getOperation().toString()); | ||
352 | switch (predicate.getOperation()) { | 367 | switch (predicate.getOperation()) { |
353 | case EQUAL: | 368 | case EQUAL: |
354 | return val.equals(predicateValue); | 369 | return val.equals(predicateValue); |
@@ -368,6 +383,8 @@ public class TriggerRuleState { | @@ -368,6 +383,8 @@ public class TriggerRuleState { | ||
368 | if (predicateValue == null) { | 383 | if (predicateValue == null) { |
369 | return false; | 384 | return false; |
370 | } | 385 | } |
386 | + detailInform.put(FastIotConstants.Alarm.VALUE,predicateValue); | ||
387 | + detailInform.put(FastIotConstants.Alarm.PREDICATE,predicate.getOperation().toString()); | ||
371 | switch (predicate.getOperation()) { | 388 | switch (predicate.getOperation()) { |
372 | case NOT_EQUAL: | 389 | case NOT_EQUAL: |
373 | return !val.equals(predicateValue); | 390 | return !val.equals(predicateValue); |
@@ -399,6 +416,8 @@ public class TriggerRuleState { | @@ -399,6 +416,8 @@ public class TriggerRuleState { | ||
399 | val = val.toLowerCase(); | 416 | val = val.toLowerCase(); |
400 | predicateValue = predicateValue.toLowerCase(); | 417 | predicateValue = predicateValue.toLowerCase(); |
401 | } | 418 | } |
419 | + detailInform.put(FastIotConstants.Alarm.VALUE,predicateValue); | ||
420 | + detailInform.put(FastIotConstants.Alarm.PREDICATE,predicate.getOperation().toString()); | ||
402 | switch (predicate.getOperation()) { | 421 | switch (predicate.getOperation()) { |
403 | case CONTAINS: | 422 | case CONTAINS: |
404 | return val.contains(predicateValue); | 423 | return val.contains(predicateValue); |