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 df46a4bf88..56fe68f010 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 @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.profile; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.action.TbAlarmResult; @@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceQueue; @@ -74,7 +76,7 @@ class AlarmState { public boolean createOrClearAlarms(TbContext ctx, T data, SnapshotUpdate update, BiFunction evalFunction) { boolean stateUpdate = false; - AlarmSeverity resultSeverity = null; + AlarmRuleState resultState = null; log.debug("[{}] processing update: {}", alarmDefinition.getId(), data); for (AlarmRuleState state : createRulesSortedBySeverityDesc) { if (!validateUpdate(update, state)) { @@ -84,15 +86,15 @@ class AlarmState { AlarmEvalResult evalResult = evalFunction.apply(state, data); stateUpdate |= state.checkUpdate(); if (AlarmEvalResult.TRUE.equals(evalResult)) { - resultSeverity = state.getSeverity(); + resultState = state; break; } else if (AlarmEvalResult.FALSE.equals(evalResult)) { state.clear(); stateUpdate |= state.checkUpdate(); } } - if (resultSeverity != null) { - TbAlarmResult result = calculateAlarmResult(ctx, resultSeverity); + if (resultState != null) { + TbAlarmResult result = calculateAlarmResult(ctx, resultState, data); if (result != null) { pushMsg(ctx, result); } @@ -186,7 +188,8 @@ class AlarmState { } } - private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) { + private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmRuleState ruleState, T data) { + AlarmSeverity severity = ruleState.getSeverity(); if (currentAlarm != null) { // TODO: In some extremely rare cases, we might miss the event of alarm clear (If one use in-mem queue and restarted the server) or (if one manipulated the rule chain). // Maybe we should fetch alarm every time? @@ -212,7 +215,7 @@ class AlarmState { currentAlarm.setSeverity(severity); currentAlarm.setStartTs(System.currentTimeMillis()); currentAlarm.setEndTs(currentAlarm.getStartTs()); - currentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.createObjectNode()); + currentAlarm.setDetails(createDetails(ruleState, (DataSnapshot) data)); currentAlarm.setOriginator(originator); currentAlarm.setTenantId(ctx.getTenantId()); currentAlarm.setPropagate(alarmDefinition.isPropagate()); @@ -225,6 +228,44 @@ class AlarmState { } } + private JsonNode createDetails(AlarmRuleState ruleState, DataSnapshot dataSnapshot) { + ObjectNode details = JacksonUtil.OBJECT_MAPPER.createObjectNode(); + String alarmDetails = ruleState.getAlarmRule().getAlarmDetails(); + + if (alarmDetails != null) { + for (KeyFilter keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) { + EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey()); + alarmDetails = alarmDetails.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue)); + } + + details.put("data", alarmDetails); + } + + return details; + } + + private static String getValueAsString(EntityKeyValue entityKeyValue) { + Object result = null; + switch (entityKeyValue.getDataType()) { + case STRING: + result = entityKeyValue.getStrValue(); + break; + case JSON: + result = entityKeyValue.getJsonValue(); + break; + case LONG: + result = String.valueOf(entityKeyValue.getLngValue()); + break; + case DOUBLE: + result = String.valueOf(entityKeyValue.getDblValue()); + break; + case BOOLEAN: + result = String.valueOf(entityKeyValue.getBoolValue()); + break; + } + return String.valueOf(result); + } + public boolean processAlarmClear(TbContext ctx, Alarm alarmNf) { boolean updated = false; if (currentAlarm != null && currentAlarm.getId().equals(alarmNf.getId())) {