Merge pull request #1807 from thingsboard/feature/period-in-seconds-pattern-alarm-originator

Added alarm originator source; Added period in seconds use metadata p…
This commit is contained in:
VoBa 2019-06-26 12:25:48 +03:00 committed by GitHub
commit 12b2aa0086
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 10 deletions

View File

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.delay; package org.thingsboard.rule.engine.delay;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -51,13 +52,11 @@ public class TbMsgDelayNode implements TbNode {
private static final String TB_MSG_DELAY_NODE_MSG = "TbMsgDelayNodeMsg"; private static final String TB_MSG_DELAY_NODE_MSG = "TbMsgDelayNodeMsg";
private TbMsgDelayNodeConfiguration config; private TbMsgDelayNodeConfiguration config;
private long delay;
private Map<UUID, TbMsg> pendingMsgs; private Map<UUID, TbMsg> pendingMsgs;
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbMsgDelayNodeConfiguration.class); this.config = TbNodeUtils.convert(configuration, TbMsgDelayNodeConfiguration.class);
this.delay = TimeUnit.SECONDS.toMillis(config.getPeriodInSeconds());
this.pendingMsgs = new HashMap<>(); this.pendingMsgs = new HashMap<>();
} }
@ -72,13 +71,31 @@ public class TbMsgDelayNode implements TbNode {
if(pendingMsgs.size() < config.getMaxPendingMsgs()) { if(pendingMsgs.size() < config.getMaxPendingMsgs()) {
pendingMsgs.put(msg.getId(), msg); pendingMsgs.put(msg.getId(), msg);
TbMsg tickMsg = ctx.newMsg(TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString()); TbMsg tickMsg = ctx.newMsg(TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString());
ctx.tellSelf(tickMsg, delay); ctx.tellSelf(tickMsg, getDelay(msg));
} else { } else {
ctx.tellNext(msg, FAILURE, new RuntimeException("Max limit of pending messages reached!")); ctx.tellNext(msg, FAILURE, new RuntimeException("Max limit of pending messages reached!"));
} }
} }
} }
private long getDelay(TbMsg msg) {
int periodInSeconds;
if (config.isUseMetadataPeriodInSecondsPatterns()) {
if (isParsable(msg, config.getPeriodInSecondsPattern())) {
periodInSeconds = Integer.parseInt(TbNodeUtils.processPattern(config.getPeriodInSecondsPattern(), msg.getMetaData()));
} else {
throw new RuntimeException("Can't parse period in seconds from metadata using pattern: " + config.getPeriodInSecondsPattern());
}
} else {
periodInSeconds = config.getPeriodInSeconds();
}
return TimeUnit.SECONDS.toMillis(periodInSeconds);
}
private boolean isParsable(TbMsg msg, String pattern) {
return NumberUtils.isParsable(TbNodeUtils.processPattern(pattern, msg.getMetaData()));
}
@Override @Override
public void destroy() { public void destroy() {
pendingMsgs.clear(); pendingMsgs.clear();

View File

@ -24,12 +24,15 @@ public class TbMsgDelayNodeConfiguration implements NodeConfiguration<TbMsgDelay
private int periodInSeconds; private int periodInSeconds;
private int maxPendingMsgs; private int maxPendingMsgs;
private String periodInSecondsPattern;
private boolean useMetadataPeriodInSecondsPatterns;
@Override @Override
public TbMsgDelayNodeConfiguration defaultConfiguration() { public TbMsgDelayNodeConfiguration defaultConfiguration() {
TbMsgDelayNodeConfiguration configuration = new TbMsgDelayNodeConfiguration(); TbMsgDelayNodeConfiguration configuration = new TbMsgDelayNodeConfiguration();
configuration.setPeriodInSeconds(60); configuration.setPeriodInSeconds(60);
configuration.setMaxPendingMsgs(1000); configuration.setMaxPendingMsgs(1000);
configuration.setUseMetadataPeriodInSecondsPatterns(false);
return configuration; return configuration;
} }
} }

View File

@ -25,6 +25,7 @@ import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.util.EntitiesAlarmOriginatorIdAsyncLoader;
import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
@ -39,9 +40,10 @@ import java.util.HashSet;
type = ComponentType.TRANSFORMATION, type = ComponentType.TRANSFORMATION,
name = "change originator", name = "change originator",
configClazz = TbChangeOriginatorNodeConfiguration.class, configClazz = TbChangeOriginatorNodeConfiguration.class,
nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity", nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity/Alarm Originator",
nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
"If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded. ", "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.<br/>" +
"Alarm Originator found only in case original Originator is <code>Alarm</code> entity.",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbTransformationNodeChangeOriginatorConfig", configDirective = "tbTransformationNodeChangeOriginatorConfig",
icon = "find_replace" icon = "find_replace"
@ -51,6 +53,7 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode {
protected static final String CUSTOMER_SOURCE = "CUSTOMER"; protected static final String CUSTOMER_SOURCE = "CUSTOMER";
protected static final String TENANT_SOURCE = "TENANT"; protected static final String TENANT_SOURCE = "TENANT";
protected static final String RELATED_SOURCE = "RELATED"; protected static final String RELATED_SOURCE = "RELATED";
protected static final String ALARM_ORIGINATOR_SOURCE = "ALARM_ORIGINATOR";
private TbChangeOriginatorNodeConfiguration config; private TbChangeOriginatorNodeConfiguration config;
@ -80,13 +83,15 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode {
return EntitiesTenantIdAsyncLoader.findEntityIdAsync(ctx, original); return EntitiesTenantIdAsyncLoader.findEntityIdAsync(ctx, original);
case RELATED_SOURCE: case RELATED_SOURCE:
return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, original, config.getRelationsQuery()); return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, original, config.getRelationsQuery());
case ALARM_ORIGINATOR_SOURCE:
return EntitiesAlarmOriginatorIdAsyncLoader.findEntityIdAsync(ctx, original);
default: default:
return Futures.immediateFailedFuture(new IllegalStateException("Unexpected originator source " + config.getOriginatorSource())); return Futures.immediateFailedFuture(new IllegalStateException("Unexpected originator source " + config.getOriginatorSource()));
} }
} }
private void validateConfig(TbChangeOriginatorNodeConfiguration conf) { private void validateConfig(TbChangeOriginatorNodeConfiguration conf) {
HashSet<String> knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE); HashSet<String> knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE);
if (!knownSources.contains(conf.getOriginatorSource())) { if (!knownSources.contains(conf.getOriginatorSource())) {
log.error("Unsupported source [{}] for TbChangeOriginatorNode", conf.getOriginatorSource()); log.error("Unsupported source [{}] for TbChangeOriginatorNode", conf.getOriginatorSource());
throw new IllegalArgumentException("Unsupported source TbChangeOriginatorNode" + conf.getOriginatorSource()); throw new IllegalArgumentException("Unsupported source TbChangeOriginatorNode" + conf.getOriginatorSource());

View File

@ -0,0 +1,44 @@
/**
* Copyright © 2016-2019 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.util;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
public class EntitiesAlarmOriginatorIdAsyncLoader {
public static ListenableFuture<EntityId> findEntityIdAsync(TbContext ctx, EntityId original) {
switch (original.getEntityType()) {
case ALARM:
return getAlarmOriginatorAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) original));
default:
return Futures.immediateFailedFuture(new TbNodeException("Unexpected original EntityType " + original));
}
}
private static ListenableFuture<EntityId> getAlarmOriginatorAsync(ListenableFuture<Alarm> future) {
return Futures.transformAsync(future, in -> {
return in != null ? Futures.immediateFuture(in.getOriginator())
: Futures.immediateFuture(null);
});
}
}