Adding ctx as first argument in CF
This commit is contained in:
parent
e2e49009a0
commit
ee3d405ed8
@ -34,6 +34,8 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.script.api.tbel.TbelCfArg;
|
import org.thingsboard.script.api.tbel.TbelCfArg;
|
||||||
|
import org.thingsboard.script.api.tbel.TbelCfCtx;
|
||||||
|
import org.thingsboard.script.api.tbel.TbelCfSingleValueArg;
|
||||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.EventInfo;
|
import org.thingsboard.server.common.data.EventInfo;
|
||||||
@ -216,11 +218,14 @@ public class CalculatedFieldController extends BaseController {
|
|||||||
@RequestBody JsonNode inputParams) {
|
@RequestBody JsonNode inputParams) {
|
||||||
String expression = inputParams.get("expression").asText();
|
String expression = inputParams.get("expression").asText();
|
||||||
Map<String, TbelCfArg> arguments = Objects.requireNonNullElse(
|
Map<String, TbelCfArg> arguments = Objects.requireNonNullElse(
|
||||||
JacksonUtil.convertValue(inputParams.get("arguments"), new TypeReference<Map<String, TbelCfArg>>() {
|
JacksonUtil.convertValue(inputParams.get("arguments"), new TypeReference<>() {
|
||||||
}),
|
}),
|
||||||
Collections.emptyMap()
|
Collections.emptyMap()
|
||||||
);
|
);
|
||||||
ArrayList<String> argNames = new ArrayList<>(arguments.keySet());
|
|
||||||
|
ArrayList<String> ctxAndArgNames = new ArrayList<>(arguments.size() + 1);
|
||||||
|
ctxAndArgNames.add("ctx");
|
||||||
|
ctxAndArgNames.addAll(arguments.keySet());
|
||||||
|
|
||||||
String output = "";
|
String output = "";
|
||||||
String errorText = "";
|
String errorText = "";
|
||||||
@ -234,12 +239,20 @@ public class CalculatedFieldController extends BaseController {
|
|||||||
getTenantId(),
|
getTenantId(),
|
||||||
tbelInvokeService,
|
tbelInvokeService,
|
||||||
expression,
|
expression,
|
||||||
argNames.toArray(String[]::new)
|
ctxAndArgNames.toArray(String[]::new)
|
||||||
);
|
);
|
||||||
|
|
||||||
Object[] args = argNames.stream()
|
|
||||||
.map(arguments::get)
|
Object[] args = new Object[ctxAndArgNames.size()];
|
||||||
.toArray();
|
args[0] = new TbelCfCtx(arguments);
|
||||||
|
for (int i = 1; i < ctxAndArgNames.size(); i++) {
|
||||||
|
var arg = arguments.get(ctxAndArgNames.get(i));
|
||||||
|
if (arg instanceof TbelCfSingleValueArg svArg) {
|
||||||
|
args[i] = svArg.getValue();
|
||||||
|
} else {
|
||||||
|
args[i] = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JsonNode json = calculatedFieldScriptEngine.executeJsonAsync(args).get(TIMEOUT, TimeUnit.SECONDS);
|
JsonNode json = calculatedFieldScriptEngine.executeJsonAsync(args).get(TIMEOUT, TimeUnit.SECONDS);
|
||||||
output = JacksonUtil.toString(json);
|
output = JacksonUtil.toString(json);
|
||||||
@ -260,7 +273,8 @@ public class CalculatedFieldController extends BaseController {
|
|||||||
EntityType entityType = referencedEntityId.getEntityType();
|
EntityType entityType = referencedEntityId.getEntityType();
|
||||||
switch (entityType) {
|
switch (entityType) {
|
||||||
case TENANT, CUSTOMER, ASSET, DEVICE -> checkEntityId(referencedEntityId, Operation.READ);
|
case TENANT, CUSTOMER, ASSET, DEVICE -> checkEntityId(referencedEntityId, Operation.READ);
|
||||||
default -> throw new IllegalArgumentException("Calculated fields do not support '" + entityType + "' for referenced entities.");
|
default ->
|
||||||
|
throw new IllegalArgumentException("Calculated fields do not support '" + entityType + "' for referenced entities.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -136,11 +136,14 @@ public class CalculatedFieldCtx {
|
|||||||
throw new IllegalArgumentException("TBEL script engine is disabled!");
|
throw new IllegalArgumentException("TBEL script engine is disabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> ctxAndArgNames = new ArrayList<>(argNames.size() + 1);
|
||||||
|
ctxAndArgNames.add("ctx");
|
||||||
|
ctxAndArgNames.addAll(argNames);
|
||||||
return new CalculatedFieldTbelScriptEngine(
|
return new CalculatedFieldTbelScriptEngine(
|
||||||
tenantId,
|
tenantId,
|
||||||
tbelInvokeService,
|
tbelInvokeService,
|
||||||
expression,
|
expression,
|
||||||
argNames.toArray(String[]::new)
|
ctxAndArgNames.toArray(String[]::new)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,11 +23,17 @@ import lombok.Data;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.thingsboard.script.api.tbel.TbelCfArg;
|
import org.thingsboard.script.api.tbel.TbelCfArg;
|
||||||
|
import org.thingsboard.script.api.tbel.TbelCfCtx;
|
||||||
|
import org.thingsboard.script.api.tbel.TbelCfSingleValueArg;
|
||||||
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.service.cf.CalculatedFieldResult;
|
import org.thingsboard.server.service.cf.CalculatedFieldResult;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -49,11 +55,20 @@ public class ScriptCalculatedFieldState extends BaseCalculatedFieldState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculatedFieldCtx ctx) {
|
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculatedFieldCtx ctx) {
|
||||||
Object[] args = ctx.getArgNames().stream()
|
Map<String, TbelCfArg> arguments = new LinkedHashMap<>();
|
||||||
.map(this::toTbelArgument)
|
List<Object> args = new ArrayList<>(ctx.getArgNames().size() + 1);
|
||||||
.toArray();
|
args.add(new Object()); // first element is a ctx, but we will set it later;
|
||||||
|
for (String argName : ctx.getArgNames()) {
|
||||||
ListenableFuture<JsonNode> resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args);
|
var arg = toTbelArgument(argName);
|
||||||
|
arguments.put(argName, arg);
|
||||||
|
if (arg instanceof TbelCfSingleValueArg svArg) {
|
||||||
|
args.add(svArg.getValue());
|
||||||
|
} else {
|
||||||
|
args.add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args.set(0, new TbelCfCtx(arguments));
|
||||||
|
ListenableFuture<JsonNode> resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args.toArray());
|
||||||
Output output = ctx.getOutput();
|
Output output = ctx.getOutput();
|
||||||
return Futures.transform(resultFuture,
|
return Futures.transform(resultFuture,
|
||||||
result -> new CalculatedFieldResult(output.getType(), output.getScope(), result),
|
result -> new CalculatedFieldResult(output.getType(), output.getScope(), result),
|
||||||
|
|||||||
@ -191,7 +191,7 @@ public class ScriptCalculatedFieldStateTest {
|
|||||||
|
|
||||||
config.setArguments(Map.of("deviceTemperature", argument1, "assetHumidity", argument2));
|
config.setArguments(Map.of("deviceTemperature", argument1, "assetHumidity", argument2));
|
||||||
|
|
||||||
config.setExpression("return {\"maxDeviceTemperature\": deviceTemperature.max(), \"assetHumidity\": assetHumidity.value}");
|
config.setExpression("return {\"maxDeviceTemperature\": deviceTemperature.max(), \"assetHumidity\": assetHumidity}");
|
||||||
|
|
||||||
Output output = new Output();
|
Output output = new Output();
|
||||||
output.setType(OutputType.ATTRIBUTES);
|
output.setType(OutputType.ATTRIBUTES);
|
||||||
|
|||||||
@ -140,7 +140,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
|
|||||||
private long maxCalculatedFieldsPerEntity = 5;
|
private long maxCalculatedFieldsPerEntity = 5;
|
||||||
@Schema(example = "10")
|
@Schema(example = "10")
|
||||||
private long maxArgumentsPerCF = 10;
|
private long maxArgumentsPerCF = 10;
|
||||||
@Min(value = 0, message = "must be at least 0")
|
@Min(value = 1, message = "must be at least 1")
|
||||||
@Schema(example = "1000")
|
@Schema(example = "1000")
|
||||||
private long maxDataPointsPerRollingArg = 1000;
|
private long maxDataPointsPerRollingArg = 1000;
|
||||||
@Schema(example = "32")
|
@Schema(example = "32")
|
||||||
|
|||||||
@ -139,6 +139,7 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem
|
|||||||
parserConfig.registerDataType("TbelCfTsRollingData", TbelCfTsRollingData.class, TbelCfTsRollingData::memorySize);
|
parserConfig.registerDataType("TbelCfTsRollingData", TbelCfTsRollingData.class, TbelCfTsRollingData::memorySize);
|
||||||
parserConfig.registerDataType("TbTimeWindow", TbTimeWindow.class, TbTimeWindow::memorySize);
|
parserConfig.registerDataType("TbTimeWindow", TbTimeWindow.class, TbTimeWindow::memorySize);
|
||||||
parserConfig.registerDataType("TbelCfTsDoubleVal", TbelCfTsMultiDoubleVal.class, TbelCfTsMultiDoubleVal::memorySize);
|
parserConfig.registerDataType("TbelCfTsDoubleVal", TbelCfTsMultiDoubleVal.class, TbelCfTsMultiDoubleVal::memorySize);
|
||||||
|
parserConfig.registerDataType("TbelCfCtx", TbelCfCtx.class, TbelCfCtx::memorySize);
|
||||||
|
|
||||||
TbUtils.register(parserConfig);
|
TbUtils.register(parserConfig);
|
||||||
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "tbel-executor"));
|
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "tbel-executor"));
|
||||||
|
|||||||
@ -18,6 +18,8 @@ package org.thingsboard.script.api.tbel;
|
|||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.mvel2.ConversionHandler;
|
||||||
|
import org.mvel2.DataConversion;
|
||||||
import org.mvel2.ExecutionContext;
|
import org.mvel2.ExecutionContext;
|
||||||
import org.mvel2.ParserConfiguration;
|
import org.mvel2.ParserConfiguration;
|
||||||
import org.mvel2.execution.ExecutionArrayList;
|
import org.mvel2.execution.ExecutionArrayList;
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2025 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.script.api.tbel;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TbelCfCtx implements TbelCfObject {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Map<String, TbelCfArg> args;
|
||||||
|
|
||||||
|
public TbelCfCtx(Map<String, TbelCfArg> args) {
|
||||||
|
this.args = Collections.unmodifiableMap(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long memorySize() {
|
||||||
|
return OBJ_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,8 @@ package org.thingsboard.script.api.tbel;
|
|||||||
|
|
||||||
public interface TbelCfObject {
|
public interface TbelCfObject {
|
||||||
|
|
||||||
|
long OBJ_SIZE = 32L; // Approximate calculation;
|
||||||
|
|
||||||
long memorySize();
|
long memorySize();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,6 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class TbelCfSingleValueArg implements TbelCfArg {
|
public class TbelCfSingleValueArg implements TbelCfArg {
|
||||||
|
|
||||||
public static final long OBJ_SIZE = 32L; // Approximate calculation;
|
|
||||||
|
|
||||||
private final long ts;
|
private final long ts;
|
||||||
private final Object value;
|
private final Object value;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user