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.thingsboard.common.util.JacksonUtil;
|
||||
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.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.EventInfo;
|
||||
@ -216,11 +218,14 @@ public class CalculatedFieldController extends BaseController {
|
||||
@RequestBody JsonNode inputParams) {
|
||||
String expression = inputParams.get("expression").asText();
|
||||
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()
|
||||
);
|
||||
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 errorText = "";
|
||||
@ -234,12 +239,20 @@ public class CalculatedFieldController extends BaseController {
|
||||
getTenantId(),
|
||||
tbelInvokeService,
|
||||
expression,
|
||||
argNames.toArray(String[]::new)
|
||||
ctxAndArgNames.toArray(String[]::new)
|
||||
);
|
||||
|
||||
Object[] args = argNames.stream()
|
||||
.map(arguments::get)
|
||||
.toArray();
|
||||
|
||||
Object[] args = new Object[ctxAndArgNames.size()];
|
||||
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);
|
||||
output = JacksonUtil.toString(json);
|
||||
@ -260,7 +273,8 @@ public class CalculatedFieldController extends BaseController {
|
||||
EntityType entityType = referencedEntityId.getEntityType();
|
||||
switch (entityType) {
|
||||
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!");
|
||||
}
|
||||
|
||||
List<String> ctxAndArgNames = new ArrayList<>(argNames.size() + 1);
|
||||
ctxAndArgNames.add("ctx");
|
||||
ctxAndArgNames.addAll(argNames);
|
||||
return new CalculatedFieldTbelScriptEngine(
|
||||
tenantId,
|
||||
tbelInvokeService,
|
||||
expression,
|
||||
argNames.toArray(String[]::new)
|
||||
ctxAndArgNames.toArray(String[]::new)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -23,11 +23,17 @@ import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.configuration.Output;
|
||||
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.Map;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@ -49,11 +55,20 @@ public class ScriptCalculatedFieldState extends BaseCalculatedFieldState {
|
||||
|
||||
@Override
|
||||
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculatedFieldCtx ctx) {
|
||||
Object[] args = ctx.getArgNames().stream()
|
||||
.map(this::toTbelArgument)
|
||||
.toArray();
|
||||
|
||||
ListenableFuture<JsonNode> resultFuture = ctx.getCalculatedFieldScriptEngine().executeJsonAsync(args);
|
||||
Map<String, TbelCfArg> arguments = new LinkedHashMap<>();
|
||||
List<Object> args = new ArrayList<>(ctx.getArgNames().size() + 1);
|
||||
args.add(new Object()); // first element is a ctx, but we will set it later;
|
||||
for (String argName : ctx.getArgNames()) {
|
||||
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();
|
||||
return Futures.transform(resultFuture,
|
||||
result -> new CalculatedFieldResult(output.getType(), output.getScope(), result),
|
||||
|
||||
@ -191,7 +191,7 @@ public class ScriptCalculatedFieldStateTest {
|
||||
|
||||
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.setType(OutputType.ATTRIBUTES);
|
||||
|
||||
@ -140,7 +140,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
|
||||
private long maxCalculatedFieldsPerEntity = 5;
|
||||
@Schema(example = "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")
|
||||
private long maxDataPointsPerRollingArg = 1000;
|
||||
@Schema(example = "32")
|
||||
|
||||
@ -139,6 +139,7 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem
|
||||
parserConfig.registerDataType("TbelCfTsRollingData", TbelCfTsRollingData.class, TbelCfTsRollingData::memorySize);
|
||||
parserConfig.registerDataType("TbTimeWindow", TbTimeWindow.class, TbTimeWindow::memorySize);
|
||||
parserConfig.registerDataType("TbelCfTsDoubleVal", TbelCfTsMultiDoubleVal.class, TbelCfTsMultiDoubleVal::memorySize);
|
||||
parserConfig.registerDataType("TbelCfCtx", TbelCfCtx.class, TbelCfCtx::memorySize);
|
||||
|
||||
TbUtils.register(parserConfig);
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.mvel2.ConversionHandler;
|
||||
import org.mvel2.DataConversion;
|
||||
import org.mvel2.ExecutionContext;
|
||||
import org.mvel2.ParserConfiguration;
|
||||
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 {
|
||||
|
||||
long OBJ_SIZE = 32L; // Approximate calculation;
|
||||
|
||||
long memorySize();
|
||||
|
||||
}
|
||||
|
||||
@ -22,8 +22,6 @@ import lombok.Data;
|
||||
@Data
|
||||
public class TbelCfSingleValueArg implements TbelCfArg {
|
||||
|
||||
public static final long OBJ_SIZE = 32L; // Approximate calculation;
|
||||
|
||||
private final long ts;
|
||||
private final Object value;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user