Browse Source

CF: expressions usage refactoring

pull/14193/head
VIacheslavKlimov 12 months ago
parent
commit
d16ee53bcd
  1. 27
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
  2. 15
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java
  3. 32
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java

27
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java

@ -176,13 +176,8 @@ public class CalculatedFieldCtx {
public void init() { public void init() {
switch (cfType) { switch (cfType) {
case SCRIPT -> { case SCRIPT -> {
try { initTbelExpression(expression);
initTbelExpression(expression); initialized = true;
initialized = true;
} catch (Exception e) {
initialized = false;
throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax.", e);
}
} }
case GEOFENCING -> initialized = true; case GEOFENCING -> initialized = true;
case SIMPLE -> { case SIMPLE -> {
@ -205,8 +200,7 @@ public class CalculatedFieldCtx {
} }
} }
public double evaluateSimpleExpression(String expressionStr, CalculatedFieldState state) { public double evaluateSimpleExpression(Expression expression, CalculatedFieldState state) {
Expression expression = simpleExpressions.get(expressionStr).get();
for (Map.Entry<String, ArgumentEntry> entry : state.getArguments().entrySet()) { for (Map.Entry<String, ArgumentEntry> entry : state.getArguments().entrySet()) {
try { try {
BasicKvEntry kvEntry = ((SingleValueArgumentEntry) entry.getValue()).getKvEntryValue(); BasicKvEntry kvEntry = ((SingleValueArgumentEntry) entry.getValue()).getKvEntryValue();
@ -225,6 +219,10 @@ public class CalculatedFieldCtx {
} }
public ListenableFuture<Object> evaluateTbelExpression(String expression, CalculatedFieldState state) { public ListenableFuture<Object> evaluateTbelExpression(String expression, CalculatedFieldState state) {
return evaluateTbelExpression(tbelExpressions.get(expression), state);
}
public ListenableFuture<Object> evaluateTbelExpression(CalculatedFieldScriptEngine expression, CalculatedFieldState state) {
Map<String, TbelCfArg> arguments = new LinkedHashMap<>(); Map<String, TbelCfArg> arguments = new LinkedHashMap<>();
List<Object> args = new ArrayList<>(argNames.size() + 1); List<Object> args = new ArrayList<>(argNames.size() + 1);
args.add(new Object()); // first element is a ctx, but we will set it later; args.add(new Object()); // first element is a ctx, but we will set it later;
@ -239,7 +237,7 @@ public class CalculatedFieldCtx {
} }
args.set(0, new TbelCfCtx(arguments, state.getLatestTimestamp())); args.set(0, new TbelCfCtx(arguments, state.getLatestTimestamp()));
return tbelExpressions.get(expression).executeScriptAsync(args.toArray()); return expression.executeScriptAsync(args.toArray());
} }
public ScheduledFuture<?> scheduleReevaluation(long delayMs, TbActorRef actorCtx) { public ScheduledFuture<?> scheduleReevaluation(long delayMs, TbActorRef actorCtx) {
@ -258,8 +256,13 @@ public class CalculatedFieldCtx {
} else if (tbelExpressions.containsKey(expression)) { } else if (tbelExpressions.containsKey(expression)) {
return; return;
} }
CalculatedFieldScriptEngine engine = initEngine(tenantId, expression, tbelInvokeService); try {
tbelExpressions.put(expression, engine); CalculatedFieldScriptEngine engine = initEngine(tenantId, expression, tbelInvokeService);
tbelExpressions.put(expression, engine);
} catch (Exception e) {
initialized = false;
throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax.", e);
}
} }
private void initSimpleExpression(String expression) { private void initSimpleExpression(String expression) {

15
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -33,18 +34,21 @@ import java.util.Map;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ScriptCalculatedFieldState extends BaseCalculatedFieldState { public class ScriptCalculatedFieldState extends BaseCalculatedFieldState {
private CalculatedFieldScriptEngine tbelExpression;
public ScriptCalculatedFieldState(EntityId entityId) { public ScriptCalculatedFieldState(EntityId entityId) {
super(entityId); super(entityId);
} }
@Override @Override
public CalculatedFieldType getType() { public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) {
return CalculatedFieldType.SCRIPT; super.setCtx(ctx, actorCtx);
this.tbelExpression = ctx.getTbelExpressions().get(ctx.getExpression());
} }
@Override @Override
public ListenableFuture<CalculatedFieldResult> performCalculation(Map<String, ArgumentEntry> updatedArgs, CalculatedFieldCtx ctx) { public ListenableFuture<CalculatedFieldResult> performCalculation(Map<String, ArgumentEntry> updatedArgs, CalculatedFieldCtx ctx) {
ListenableFuture<Object> resultFuture = ctx.evaluateTbelExpression(ctx.getExpression(), this); ListenableFuture<Object> resultFuture = ctx.evaluateTbelExpression(tbelExpression, this);
Output output = ctx.getOutput(); Output output = ctx.getOutput();
return Futures.transform(resultFuture, return Futures.transform(resultFuture,
result -> TelemetryCalculatedFieldResult.builder() result -> TelemetryCalculatedFieldResult.builder()
@ -56,4 +60,9 @@ public class ScriptCalculatedFieldState extends BaseCalculatedFieldState {
); );
} }
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.SCRIPT;
}
} }

32
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java

@ -20,8 +20,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import net.objecthunter.exp4j.Expression;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.script.api.tbel.TbUtils; import org.thingsboard.script.api.tbel.TbUtils;
import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.Output; import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -33,26 +35,21 @@ import java.util.Map;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SimpleCalculatedFieldState extends BaseCalculatedFieldState { public class SimpleCalculatedFieldState extends BaseCalculatedFieldState {
private ThreadLocal<Expression> expression;
public SimpleCalculatedFieldState(EntityId entityId) { public SimpleCalculatedFieldState(EntityId entityId) {
super(entityId); super(entityId);
} }
@Override @Override
public CalculatedFieldType getType() { public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) {
return CalculatedFieldType.SIMPLE; super.setCtx(ctx, actorCtx);
} this.expression = ctx.getSimpleExpressions().get(ctx.getExpression());
@Override
protected void validateNewEntry(String key, ArgumentEntry newEntry) {
if (newEntry instanceof TsRollingArgumentEntry) {
throw new IllegalArgumentException("Unsupported argument type detected for argument: " + key + ". " +
"Rolling argument entry is not supported for simple calculated fields.");
}
} }
@Override @Override
public ListenableFuture<CalculatedFieldResult> performCalculation(Map<String, ArgumentEntry> updatedArgs, CalculatedFieldCtx ctx) { public ListenableFuture<CalculatedFieldResult> performCalculation(Map<String, ArgumentEntry> updatedArgs, CalculatedFieldCtx ctx) {
double expressionResult = ctx.evaluateSimpleExpression(ctx.getExpression(), this); double expressionResult = ctx.evaluateSimpleExpression(expression.get(), this);
Output output = ctx.getOutput(); Output output = ctx.getOutput();
Object result = formatResult(expressionResult, output.getDecimalsByDefault()); Object result = formatResult(expressionResult, output.getDecimalsByDefault());
@ -96,4 +93,17 @@ public class SimpleCalculatedFieldState extends BaseCalculatedFieldState {
} }
} }
@Override
protected void validateNewEntry(String key, ArgumentEntry newEntry) {
if (newEntry instanceof TsRollingArgumentEntry) {
throw new IllegalArgumentException("Unsupported argument type detected for argument: " + key + ". " +
"Rolling argument entry is not supported for simple calculated fields.");
}
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.SIMPLE;
}
} }

Loading…
Cancel
Save