added implementation for script type
This commit is contained in:
parent
40299cab1a
commit
c75603f57c
@ -17,6 +17,7 @@ package org.thingsboard.server.service.cf;
|
||||
|
||||
import org.thingsboard.server.common.data.id.CalculatedFieldId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||
|
||||
@ -26,8 +27,6 @@ public interface CalculatedFieldExecutionService {
|
||||
|
||||
void onCalculatedFieldMsg(TransportProtos.CalculatedFieldMsgProto proto, TbCallback callback);
|
||||
|
||||
void onTelemetryUpdate(TenantId tenantId, CalculatedFieldId calculatedFieldId, Map<String, String> updatedTelemetry);
|
||||
|
||||
// void onEntityProfileUpdate(TransportProtos.CalculatedFieldEntityProfileUpdateMsgProto proto, TbCallback callback);
|
||||
void onTelemetryUpdate(TenantId tenantId, CalculatedFieldId calculatedFieldId, Map<String, KvEntry> updatedTelemetry);
|
||||
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ public class CalculatedFieldResult {
|
||||
|
||||
private String type;
|
||||
private AttributeScope scope;
|
||||
private Map<String, String> resultMap;
|
||||
private Map<String, Object> resultMap;
|
||||
|
||||
public CalculatedFieldResult() {
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.cf;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
@ -30,6 +31,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
|
||||
@ -90,6 +92,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
private final TimeseriesService timeseriesService;
|
||||
private final RocksDBService rocksDBService;
|
||||
private final TbClusterService clusterService;
|
||||
private final TbelInvokeService tbelInvokeService;
|
||||
|
||||
private ListeningExecutorService calculatedFieldExecutor;
|
||||
private ListeningExecutorService calculatedFieldCallbackExecutor;
|
||||
@ -201,7 +204,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTelemetryUpdate(TenantId tenantId, CalculatedFieldId calculatedFieldId, Map<String, String> updatedTelemetry) {
|
||||
public void onTelemetryUpdate(TenantId tenantId, CalculatedFieldId calculatedFieldId, Map<String, KvEntry> updatedTelemetry) {
|
||||
try {
|
||||
CalculatedField calculatedField = calculatedFields.computeIfAbsent(calculatedFieldId, id -> calculatedFieldService.findById(tenantId, id));
|
||||
updateOrInitializeState(calculatedField, calculatedField.getEntityId(), updatedTelemetry);
|
||||
@ -266,13 +269,13 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
|
||||
private void initializeStateForEntity(TenantId tenantId, CalculatedField calculatedField, EntityId entityId, TbCallback callback) {
|
||||
Map<String, Argument> arguments = calculatedField.getConfiguration().getArguments();
|
||||
Map<String, String> argumentValues = new HashMap<>();
|
||||
Map<String, KvEntry> argumentValues = new HashMap<>();
|
||||
AtomicInteger remaining = new AtomicInteger(arguments.size());
|
||||
arguments.forEach((key, argument) -> Futures.addCallback(fetchArgumentValue(tenantId, argument), new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(Optional<? extends KvEntry> result) {
|
||||
String value = result.map(KvEntry::getValueAsString).orElse(argument.getDefaultValue());
|
||||
argumentValues.put(key, value);
|
||||
// todo: should be rewritten implementation for default value
|
||||
argumentValues.put(key, result.orElse(null));
|
||||
if (remaining.decrementAndGet() == 0) {
|
||||
updateOrInitializeState(calculatedField, entityId, argumentValues);
|
||||
}
|
||||
@ -300,7 +303,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
};
|
||||
}
|
||||
|
||||
private void updateOrInitializeState(CalculatedField calculatedField, EntityId entityId, Map<String, String> argumentValues) {
|
||||
private void updateOrInitializeState(CalculatedField calculatedField, EntityId entityId, Map<String, KvEntry> argumentValues) {
|
||||
CalculatedFieldCtxId ctxId = new CalculatedFieldCtxId(calculatedField.getUuidId(), entityId.getId());
|
||||
CalculatedFieldCtx calculatedFieldCtx = states.computeIfAbsent(ctxId, ctx -> new CalculatedFieldCtx(ctxId, null));
|
||||
|
||||
@ -314,10 +317,21 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
states.put(ctxId, calculatedFieldCtx);
|
||||
rocksDBService.put(JacksonUtil.writeValueAsString(ctxId), JacksonUtil.writeValueAsString(calculatedFieldCtx));
|
||||
|
||||
CalculatedFieldResult result = state.performCalculation(calculatedField.getConfiguration());
|
||||
if (result != null) {
|
||||
pushMsgToRuleEngine(calculatedField.getTenantId(), calculatedField.getEntityId(), result);
|
||||
}
|
||||
ListenableFuture<CalculatedFieldResult> resultFuture = state.performCalculation(calculatedField.getTenantId(), calculatedField.getConfiguration(), tbelInvokeService);
|
||||
Futures.addCallback(resultFuture, new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(CalculatedFieldResult result) {
|
||||
if (result != null) {
|
||||
pushMsgToRuleEngine(calculatedField.getTenantId(), calculatedField.getEntityId(), result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
log.warn("[{}] Failed to perform calculation. entityId: [{}]", calculatedField.getId(), entityId, t);
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
|
||||
}
|
||||
|
||||
private void pushMsgToRuleEngine(TenantId tenantId, EntityId originatorId, CalculatedFieldResult calculatedFieldResult) {
|
||||
@ -325,8 +339,8 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
String type = calculatedFieldResult.getType();
|
||||
TbMsgType msgType = "ATTRIBUTES".equals(type) ? TbMsgType.POST_ATTRIBUTES_REQUEST : TbMsgType.POST_TELEMETRY_REQUEST;
|
||||
TbMsgMetaData md = "ATTRIBUTES".equals(type) ? new TbMsgMetaData(Map.of(SCOPE, calculatedFieldResult.getScope().name())) : TbMsgMetaData.EMPTY;
|
||||
ObjectNode jsonNodes = createJsonPayload(calculatedFieldResult);
|
||||
TbMsg msg = TbMsg.newMsg(msgType, originatorId, md, JacksonUtil.writeValueAsString(jsonNodes));
|
||||
ObjectNode payload = createJsonPayload(calculatedFieldResult);
|
||||
TbMsg msg = TbMsg.newMsg(msgType, originatorId, md, JacksonUtil.writeValueAsString(payload));
|
||||
clusterService.pushMsgToRuleEngine(tenantId, originatorId, msg, null);
|
||||
} catch (Exception e) {
|
||||
log.warn("[{}] Failed to push message to rule engine. CalculatedFieldResult: {}", originatorId, calculatedFieldResult, e);
|
||||
@ -334,9 +348,10 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
|
||||
}
|
||||
|
||||
private ObjectNode createJsonPayload(CalculatedFieldResult calculatedFieldResult) {
|
||||
ObjectNode jsonNodes = JacksonUtil.newObjectNode();
|
||||
calculatedFieldResult.getResultMap().forEach(jsonNodes::put);
|
||||
return jsonNodes;
|
||||
ObjectNode payload = JacksonUtil.newObjectNode();
|
||||
Map<String, Object> resultMap = calculatedFieldResult.getResultMap();
|
||||
resultMap.forEach((k, v) -> payload.set(k, JacksonUtil.convertValue(v, JsonNode.class)));
|
||||
return payload;
|
||||
}
|
||||
|
||||
private CalculatedFieldState createStateByType(CalculatedFieldType calculatedFieldType) {
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.server.service.cf.ctx.state;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface CalculatedFieldScriptEngine {
|
||||
|
||||
ListenableFuture<Object> executeScriptAsync(Map<String, KvEntry> arguments);
|
||||
|
||||
void destroy();
|
||||
|
||||
}
|
||||
@ -18,9 +18,13 @@ package org.thingsboard.server.service.cf.ctx.state;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.service.cf.CalculatedFieldResult;
|
||||
|
||||
import java.util.Map;
|
||||
@ -39,12 +43,12 @@ public interface CalculatedFieldState {
|
||||
@JsonIgnore
|
||||
CalculatedFieldType getType();
|
||||
|
||||
default boolean isValid(Map<String, String> argumentValues, Map<String, Argument> arguments) {
|
||||
default boolean isValid(Map<String, KvEntry> argumentValues, Map<String, Argument> arguments) {
|
||||
return argumentValues.keySet().containsAll(arguments.keySet());
|
||||
}
|
||||
|
||||
void initState(Map<String, String> argumentValues);
|
||||
void initState(Map<String, KvEntry> argumentValues);
|
||||
|
||||
CalculatedFieldResult performCalculation(CalculatedFieldConfiguration calculatedFieldConfiguration);
|
||||
ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.server.service.cf.ctx.state;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.script.api.ScriptType;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@Slf4j
|
||||
public class CalculatedFieldTbelScriptEngine implements CalculatedFieldScriptEngine {
|
||||
|
||||
private final TbelInvokeService tbelInvokeService;
|
||||
|
||||
private final UUID scriptId;
|
||||
private final TenantId tenantId;
|
||||
|
||||
public CalculatedFieldTbelScriptEngine(TenantId tenantId, TbelInvokeService tbelInvokeService, String script, String... argNames) {
|
||||
this.tenantId = tenantId;
|
||||
this.tbelInvokeService = tbelInvokeService;
|
||||
try {
|
||||
this.scriptId = this.tbelInvokeService.eval(tenantId, ScriptType.CALCULATED_FIELD_SCRIPT, script, argNames).get();
|
||||
} catch (Exception e) {
|
||||
Throwable t = e;
|
||||
if (e instanceof ExecutionException) {
|
||||
t = e.getCause();
|
||||
}
|
||||
throw new IllegalArgumentException("Can't compile script: " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<Object> executeScriptAsync(Map<String, KvEntry> arguments) {
|
||||
log.trace("execute script async, arguments {}", arguments);
|
||||
Object[] args = new Object[arguments.size()];
|
||||
int index = 0;
|
||||
for (KvEntry entry : arguments.values()) {
|
||||
switch (entry.getDataType()) {
|
||||
case BOOLEAN -> args[index] = entry.getBooleanValue().orElse(null);
|
||||
case DOUBLE -> args[index] = entry.getDoubleValue().orElse(null);
|
||||
case LONG -> args[index] = entry.getLongValue().orElse(null);
|
||||
case JSON -> args[index] = entry.getJsonValue().map(JacksonUtil::toJsonNode).orElse(null);
|
||||
default -> args[index] = entry.getValueAsString();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return Futures.transformAsync(tbelInvokeService.invokeScript(tenantId, null, this.scriptId, args),
|
||||
o -> {
|
||||
try {
|
||||
return Futures.immediateFuture(o);
|
||||
} catch (Exception e) {
|
||||
if (e.getCause() instanceof ScriptException) {
|
||||
return Futures.immediateFailedFuture(e.getCause());
|
||||
} else if (e.getCause() instanceof RuntimeException) {
|
||||
return Futures.immediateFailedFuture(new ScriptException(e.getCause().getMessage()));
|
||||
} else {
|
||||
return Futures.immediateFailedFuture(new ScriptException(e));
|
||||
}
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
tbelInvokeService.release(this.scriptId);
|
||||
}
|
||||
}
|
||||
@ -15,10 +15,19 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.cf.ctx.state;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Output;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.service.cf.CalculatedFieldResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -28,7 +37,10 @@ import java.util.Map;
|
||||
@Slf4j
|
||||
public class ScriptCalculatedFieldState implements CalculatedFieldState {
|
||||
|
||||
private Map<String, String> arguments = new HashMap<>();
|
||||
@JsonIgnore
|
||||
private CalculatedFieldScriptEngine calculatedFieldScriptEngine;
|
||||
|
||||
private Map<String, KvEntry> arguments = new HashMap<>();
|
||||
|
||||
public ScriptCalculatedFieldState() {
|
||||
}
|
||||
@ -39,7 +51,7 @@ public class ScriptCalculatedFieldState implements CalculatedFieldState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initState(Map<String, String> argumentValues) {
|
||||
public void initState(Map<String, KvEntry> argumentValues) {
|
||||
if (arguments == null) {
|
||||
this.arguments = new HashMap<>();
|
||||
}
|
||||
@ -47,9 +59,46 @@ public class ScriptCalculatedFieldState implements CalculatedFieldState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatedFieldResult performCalculation(CalculatedFieldConfiguration calculatedFieldConfiguration) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
public ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService) {
|
||||
if (tbelInvokeService == null) {
|
||||
throw new IllegalArgumentException("TBEL script engine is disabled!");
|
||||
}
|
||||
|
||||
if (calculatedFieldScriptEngine == null) {
|
||||
initEngine(tenantId, calculatedFieldConfiguration, tbelInvokeService);
|
||||
}
|
||||
|
||||
ListenableFuture<Object> resultFuture = calculatedFieldScriptEngine.executeScriptAsync(arguments);
|
||||
|
||||
return Futures.transform(resultFuture, result -> {
|
||||
Output output = calculatedFieldConfiguration.getOutput();
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
|
||||
if (result instanceof Map<?, ?>) {
|
||||
Map<String, Object> map = JacksonUtil.convertValue(result, Map.class);
|
||||
if (map != null) {
|
||||
resultMap.putAll(map);
|
||||
}
|
||||
} else {
|
||||
resultMap.put(output.getName(), JacksonUtil.convertValue(result, Object.class));
|
||||
}
|
||||
|
||||
CalculatedFieldResult calculatedFieldResult = new CalculatedFieldResult();
|
||||
calculatedFieldResult.setType(output.getType());
|
||||
calculatedFieldResult.setScope(output.getScope());
|
||||
calculatedFieldResult.setResultMap(resultMap);
|
||||
|
||||
return calculatedFieldResult;
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
private void initEngine(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService) {
|
||||
calculatedFieldScriptEngine = new CalculatedFieldTbelScriptEngine(
|
||||
tenantId,
|
||||
tbelInvokeService,
|
||||
calculatedFieldConfiguration.getOutput().getExpression(),
|
||||
arguments.keySet().toArray(new String[0])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,13 +15,18 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.cf.ctx.state;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.Data;
|
||||
import net.objecthunter.exp4j.Expression;
|
||||
import net.objecthunter.exp4j.ExpressionBuilder;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Output;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.service.cf.CalculatedFieldResult;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -30,8 +35,7 @@ import java.util.Map;
|
||||
@Data
|
||||
public class SimpleCalculatedFieldState implements CalculatedFieldState {
|
||||
|
||||
// TODO: use value object(TsKv) instead of string
|
||||
private Map<String, String> arguments;
|
||||
private Map<String, KvEntry> arguments;
|
||||
|
||||
@Override
|
||||
public CalculatedFieldType getType() {
|
||||
@ -39,7 +43,7 @@ public class SimpleCalculatedFieldState implements CalculatedFieldState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initState(Map<String, String> argumentValues) {
|
||||
public void initState(Map<String, KvEntry> argumentValues) {
|
||||
if (arguments == null) {
|
||||
arguments = new HashMap<>();
|
||||
}
|
||||
@ -47,7 +51,7 @@ public class SimpleCalculatedFieldState implements CalculatedFieldState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatedFieldResult performCalculation(CalculatedFieldConfiguration calculatedFieldConfiguration) {
|
||||
public ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService) {
|
||||
Output output = calculatedFieldConfiguration.getOutput();
|
||||
Map<String, Argument> arguments = calculatedFieldConfiguration.getArguments();
|
||||
|
||||
@ -64,19 +68,17 @@ public class SimpleCalculatedFieldState implements CalculatedFieldState {
|
||||
customExpression.set(expr);
|
||||
}
|
||||
Map<String, Double> variables = new HashMap<>();
|
||||
this.arguments.forEach((k, v) -> variables.put(k, Double.parseDouble(v)));
|
||||
this.arguments.forEach((k, v) -> variables.put(k, Double.parseDouble(v.getValueAsString())));
|
||||
expr.setVariables(variables);
|
||||
|
||||
String expressionResult = String.valueOf(expr.evaluate());
|
||||
double expressionResult = expr.evaluate();
|
||||
|
||||
result.setType(output.getType());
|
||||
result.setScope(output.getScope());
|
||||
result.setResultMap(Map.of(output.getName(), expressionResult));
|
||||
return result;
|
||||
return Futures.immediateFuture(result);
|
||||
}
|
||||
|
||||
return null;
|
||||
// TODO: handle what happens when not valid
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -69,6 +69,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 27.03.18.
|
||||
@ -207,32 +208,28 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
|
||||
CalculatedFieldId calculatedFieldId = link.getCalculatedFieldId();
|
||||
Map<String, String> attributes = link.getConfiguration().getAttributes();
|
||||
Map<String, String> timeSeries = link.getConfiguration().getTimeSeries();
|
||||
List<? extends KvEntry> filteredTelemetry = telemetry.stream()
|
||||
Map<String, KvEntry> updatedTelemetry = telemetry.stream()
|
||||
.filter(entry -> attributes.containsValue(entry.getKey()) || timeSeries.containsValue(entry.getKey()))
|
||||
.toList();
|
||||
|
||||
|
||||
Map<String, String> updatedTelemetry = new HashMap<>();
|
||||
for (KvEntry telemetryEntry : filteredTelemetry) {
|
||||
String key = telemetryEntry.getKey();
|
||||
if (telemetryEntry instanceof AttributeKvEntry) {
|
||||
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
|
||||
if (telemetryEntry.getKey().equals(attribute.getValue())) {
|
||||
key = attribute.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (telemetryEntry instanceof TsKvEntry) {
|
||||
for (Map.Entry<String, String> timeSeriesEntry : timeSeries.entrySet()) {
|
||||
if (telemetryEntry.getKey().equals(timeSeriesEntry.getValue())) {
|
||||
key = timeSeriesEntry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedTelemetry.put(key, telemetryEntry.getValueAsString());
|
||||
}
|
||||
.collect(Collectors.toMap(
|
||||
entry -> {
|
||||
if (entry instanceof AttributeKvEntry) {
|
||||
return attributes.entrySet().stream()
|
||||
.filter(attr -> attr.getValue().equals(entry.getKey()))
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(entry.getKey());
|
||||
} else if (entry instanceof TsKvEntry) {
|
||||
return timeSeries.entrySet().stream()
|
||||
.filter(ts -> ts.getValue().equals(entry.getKey()))
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(entry.getKey());
|
||||
}
|
||||
return entry.getKey();
|
||||
},
|
||||
entry -> entry,
|
||||
(v1, v2) -> v1
|
||||
));
|
||||
|
||||
if (!updatedTelemetry.isEmpty()) {
|
||||
calculatedFieldExecutionService.onTelemetryUpdate(tenantId, calculatedFieldId, updatedTelemetry);
|
||||
|
||||
@ -16,5 +16,5 @@
|
||||
package org.thingsboard.script.api;
|
||||
|
||||
public enum ScriptType {
|
||||
RULE_NODE_SCRIPT
|
||||
RULE_NODE_SCRIPT, CALCULATED_FIELD_SCRIPT
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user