Browse Source

added nofitication on update in telemetry service

pull/12092/head
IrynaMatveieva 2 years ago
parent
commit
40299cab1a
  1. 8
      application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldExecutionService.java
  2. 23
      application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java
  3. 70
      application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldExecutionService.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/cf/RocksDBService.java
  5. 5
      application/src/main/java/org/thingsboard/server/service/cf/ctx/CalculatedFieldCtx.java
  6. 2
      application/src/main/java/org/thingsboard/server/service/cf/ctx/CalculatedFieldCtxId.java
  7. 14
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java
  8. 19
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java
  9. 38
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/cf/DefaultTbCalculatedFieldService.java
  11. 60
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  12. 9
      application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java
  13. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java
  14. 2
      common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java
  15. 8
      common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLinkConfiguration.java
  16. 4
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java
  17. 43
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java
  18. 9
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java
  19. 29
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java
  20. 40
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java
  21. 3
      common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java
  22. 16
      dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java
  23. 3
      dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldLinkDao.java
  24. 7
      dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldEntity.java
  25. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldLinkRepository.java
  26. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java
  27. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldLinkDao.java
  28. 7
      dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java
  29. 9
      dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java
  30. 7
      dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java
  31. 7
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java

8
application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldExecutionService.java

@ -15,11 +15,19 @@
*/
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.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Map;
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);
}

23
application/src/main/java/org/thingsboard/server/service/entitiy/cf/CalculatedFieldResult.java → application/src/main/java/org/thingsboard/server/service/cf/CalculatedFieldResult.java

@ -13,34 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf;
import lombok.Data;
import org.thingsboard.server.common.data.AttributeScope;
import java.util.Map;
@Data
public class CalculatedFieldResult {
private String name;
private String type;
private AttributeScope scope;
private String value;
public static CalculatedFieldResult createAttributesResult(String name, AttributeScope scope, String value) {
CalculatedFieldResult result = new CalculatedFieldResult();
result.name = name;
result.type = "ATTRIBUTES";
result.scope = scope;
result.value = value;
return result;
}
private Map<String, String> resultMap;
public static CalculatedFieldResult createTimeSeriesResult(String name, String value) {
CalculatedFieldResult result = new CalculatedFieldResult();
result.name = name;
result.type = "TIME_SERIES";
result.value = value;
return result;
public CalculatedFieldResult() {
}
}

70
application/src/main/java/org/thingsboard/server/service/cf/DefaultCalculatedFieldExecutionService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.cf;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -29,13 +30,12 @@ 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.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.BaseCalculatedFieldConfiguration;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
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.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
@ -44,7 +44,10 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.asset.AssetService;
@ -54,12 +57,11 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.cf.CalculatedFieldCtx;
import org.thingsboard.server.service.entitiy.cf.CalculatedFieldCtxId;
import org.thingsboard.server.service.entitiy.cf.CalculatedFieldState;
import org.thingsboard.server.service.entitiy.cf.RocksDBService;
import org.thingsboard.server.service.entitiy.cf.ScriptCalculatedFieldState;
import org.thingsboard.server.service.entitiy.cf.SimpleCalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldCtx;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldCtxId;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.state.ScriptCalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.state.SimpleCalculatedFieldState;
import org.thingsboard.server.service.partition.AbstractPartitionBasedService;
import java.util.ArrayList;
@ -71,6 +73,9 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.common.data.DataConstants.SCOPE;
@TbCoreComponent
@Service
@ -84,6 +89,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
private final AttributesService attributesService;
private final TimeseriesService timeseriesService;
private final RocksDBService rocksDBService;
private final TbClusterService clusterService;
private ListeningExecutorService calculatedFieldExecutor;
private ListeningExecutorService calculatedFieldCallbackExecutor;
@ -194,6 +200,17 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
}
}
@Override
public void onTelemetryUpdate(TenantId tenantId, CalculatedFieldId calculatedFieldId, Map<String, String> updatedTelemetry) {
try {
CalculatedField calculatedField = calculatedFields.computeIfAbsent(calculatedFieldId, id -> calculatedFieldService.findById(tenantId, id));
updateOrInitializeState(calculatedField, calculatedField.getEntityId(), updatedTelemetry);
log.info("Successfully updated time series for calculatedFieldId: [{}]", calculatedFieldId);
} catch (Exception e) {
log.trace("Failed to update time series for calculatedFieldId: [{}]", calculatedFieldId, e);
}
}
private boolean onCalculatedFieldUpdate(CalculatedField newCalculatedField, TbCallback callback) {
CalculatedField oldCalculatedField = calculatedFields.get(newCalculatedField.getId());
boolean shouldReinit = true;
@ -250,11 +267,15 @@ 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<>();
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);
if (remaining.decrementAndGet() == 0) {
updateOrInitializeState(calculatedField, entityId, argumentValues);
}
}
@Override
@ -263,15 +284,12 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
callback.onFailure(t);
}
}, calculatedFieldCallbackExecutor));
updateOrInitializeState(calculatedField, entityId, argumentValues);
}
private ListenableFuture<Optional<? extends KvEntry>> fetchArgumentValue(TenantId tenantId, Argument argument) {
return switch (argument.getType()) {
case "ATTRIBUTES" -> Futures.transform(
attributesService.find(tenantId, argument.getEntityId(), AttributeScope.SERVER_SCOPE, argument.getKey()),
attributesService.find(tenantId, argument.getEntityId(), argument.getScope(), argument.getKey()),
result -> result.map(entry -> (KvEntry) entry),
MoreExecutors.directExecutor());
case "TIME_SERIES" -> Futures.transform(
@ -296,7 +314,29 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
states.put(ctxId, calculatedFieldCtx);
rocksDBService.put(JacksonUtil.writeValueAsString(ctxId), JacksonUtil.writeValueAsString(calculatedFieldCtx));
state.performCalculation(calculatedField.getConfiguration());
CalculatedFieldResult result = state.performCalculation(calculatedField.getConfiguration());
if (result != null) {
pushMsgToRuleEngine(calculatedField.getTenantId(), calculatedField.getEntityId(), result);
}
}
private void pushMsgToRuleEngine(TenantId tenantId, EntityId originatorId, CalculatedFieldResult calculatedFieldResult) {
try {
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));
clusterService.pushMsgToRuleEngine(tenantId, originatorId, msg, null);
} catch (Exception e) {
log.warn("[{}] Failed to push message to rule engine. CalculatedFieldResult: {}", originatorId, calculatedFieldResult, e);
}
}
private ObjectNode createJsonPayload(CalculatedFieldResult calculatedFieldResult) {
ObjectNode jsonNodes = JacksonUtil.newObjectNode();
calculatedFieldResult.getResultMap().forEach(jsonNodes::put);
return jsonNodes;
}
private CalculatedFieldState createStateByType(CalculatedFieldType calculatedFieldType) {

4
application/src/main/java/org/thingsboard/server/service/entitiy/cf/RocksDBService.java → application/src/main/java/org/thingsboard/server/service/cf/RocksDBService.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf;
import lombok.extern.slf4j.Slf4j;
import org.rocksdb.RocksDB;
@ -94,4 +94,4 @@ public class RocksDBService {
return map;
}
}
}

5
application/src/main/java/org/thingsboard/server/service/entitiy/cf/CalculatedFieldCtx.java → application/src/main/java/org/thingsboard/server/service/cf/ctx/CalculatedFieldCtx.java

@ -13,11 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf.ctx;
import lombok.Data;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState;
@Data
public class CalculatedFieldCtx {

2
application/src/main/java/org/thingsboard/server/service/entitiy/cf/CalculatedFieldCtxId.java → application/src/main/java/org/thingsboard/server/service/cf/ctx/CalculatedFieldCtxId.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf.ctx;
import java.util.UUID;

14
application/src/main/java/org/thingsboard/server/service/entitiy/cf/CalculatedFieldState.java → application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldState.java

@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
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 org.thingsboard.server.common.data.cf.BaseCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
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.service.cf.CalculatedFieldResult;
import java.util.Map;
@ -30,15 +31,16 @@ import java.util.Map;
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = SimpleCalculatedFieldState.class, name = "SIMPLE")
@JsonSubTypes.Type(value = SimpleCalculatedFieldState.class, name = "SIMPLE"),
@JsonSubTypes.Type(value = ScriptCalculatedFieldState.class, name = "SCRIPT")
})
public interface CalculatedFieldState {
@JsonIgnore
CalculatedFieldType getType();
default boolean isValid(Map<String, String> arguments, CalculatedFieldConfiguration calculatedFieldConfiguration) {
return arguments.keySet().containsAll(calculatedFieldConfiguration.getArguments().keySet());
default boolean isValid(Map<String, String> argumentValues, Map<String, Argument> arguments) {
return argumentValues.keySet().containsAll(arguments.keySet());
}
void initState(Map<String, String> argumentValues);

19
application/src/main/java/org/thingsboard/server/service/entitiy/cf/ScriptCalculatedFieldState.java → application/src/main/java/org/thingsboard/server/service/cf/ctx/state/ScriptCalculatedFieldState.java

@ -13,23 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf.ctx.state;
import lombok.Data;
import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
import java.util.HashMap;
import java.util.Map;
@Data
@Slf4j
public class ScriptCalculatedFieldState implements CalculatedFieldState {
private TbelInvokeService tbelInvokeService;
private Map<String, String> arguments = new HashMap<>();
public ScriptCalculatedFieldState() {
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.SCRIPT;
@ -37,11 +40,15 @@ public class ScriptCalculatedFieldState implements CalculatedFieldState {
@Override
public void initState(Map<String, String> argumentValues) {
if (arguments == null) {
this.arguments = new HashMap<>();
}
this.arguments.putAll(argumentValues);
}
@Override
public CalculatedFieldResult performCalculation(CalculatedFieldConfiguration calculatedFieldConfiguration) {
// TODO: implement
return null;
}

38
application/src/main/java/org/thingsboard/server/service/entitiy/cf/SimpleCalculatedFieldState.java → application/src/main/java/org/thingsboard/server/service/cf/ctx/state/SimpleCalculatedFieldState.java

@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.cf;
package org.thingsboard.server.service.cf.ctx.state;
import lombok.Data;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
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.service.cf.CalculatedFieldResult;
import java.util.HashMap;
import java.util.Map;
@ -28,8 +31,7 @@ import java.util.Map;
public class SimpleCalculatedFieldState implements CalculatedFieldState {
// TODO: use value object(TsKv) instead of string
private Map<String, String> arguments = new HashMap<>();
private String outputResult;
private Map<String, String> arguments;
@Override
public CalculatedFieldType getType() {
@ -38,29 +40,43 @@ public class SimpleCalculatedFieldState implements CalculatedFieldState {
@Override
public void initState(Map<String, String> argumentValues) {
this.arguments = argumentValues;
if (arguments == null) {
arguments = new HashMap<>();
}
arguments.putAll(argumentValues);
}
@Override
public CalculatedFieldResult performCalculation(CalculatedFieldConfiguration calculatedFieldConfiguration) {
if (isValid(arguments, calculatedFieldConfiguration)) {
String expression = calculatedFieldConfiguration.getOutput().getExpression();
Output output = calculatedFieldConfiguration.getOutput();
Map<String, Argument> arguments = calculatedFieldConfiguration.getArguments();
if (isValid(this.arguments, arguments)) {
CalculatedFieldResult result = new CalculatedFieldResult();
String expression = output.getExpression();
ThreadLocal<Expression> customExpression = new ThreadLocal<>();
var expr = customExpression.get();
if (expr == null) {
expr = new ExpressionBuilder(expression)
.implicitMultiplication(true)
.variables(arguments.keySet())
.variables(this.arguments.keySet())
.build();
customExpression.set(expr);
}
Map<String, Double> variables = new HashMap<>();
arguments.forEach((k, v) -> variables.put(k, Double.parseDouble(v)));
this.arguments.forEach((k, v) -> variables.put(k, Double.parseDouble(v)));
expr.setVariables(variables);
double result = expr.evaluate();
this.outputResult = Double.toString(result);
String expressionResult = String.valueOf(expr.evaluate());
result.setType(output.getType());
result.setScope(output.getScope());
result.setResultMap(Map.of(output.getName(), expressionResult));
return result;
}
return null;
// TODO: handle what happens when not valid
}
}

2
application/src/main/java/org/thingsboard/server/service/entitiy/cf/DefaultTbCalculatedFieldService.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;

60
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -32,6 +32,8 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
@ -40,6 +42,7 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -47,9 +50,11 @@ import org.thingsboard.server.common.data.kv.TsKvLatestRemovingResult;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.KvUtils;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.cf.CalculatedFieldExecutionService;
import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
@ -77,6 +82,8 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
private final TbEntityViewService tbEntityViewService;
private final TbApiUsageReportClient apiUsageClient;
private final TbApiUsageStateService apiUsageStateService;
private final CalculatedFieldService calculatedFieldService;
private final CalculatedFieldExecutionService calculatedFieldExecutionService;
private ExecutorService tsCallBackExecutor;
@ -87,12 +94,16 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
TimeseriesService tsService,
@Lazy TbEntityViewService tbEntityViewService,
TbApiUsageReportClient apiUsageClient,
TbApiUsageStateService apiUsageStateService) {
TbApiUsageStateService apiUsageStateService,
CalculatedFieldService calculatedFieldService,
CalculatedFieldExecutionService calculatedFieldExecutionService) {
this.attrService = attrService;
this.tsService = tsService;
this.tbEntityViewService = tbEntityViewService;
this.apiUsageClient = apiUsageClient;
this.apiUsageStateService = apiUsageStateService;
this.calculatedFieldService = calculatedFieldService;
this.calculatedFieldExecutionService = calculatedFieldExecutionService;
}
@PostConstruct
@ -179,6 +190,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
addMainCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts));
addEntityViewCallback(tenantId, entityId, ts);
updateTelemetryInCalculatedFields(tenantId, entityId, ts);
}
private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Integer> callback) {
@ -187,6 +199,49 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts));
}
private void updateTelemetryInCalculatedFields(TenantId tenantId, EntityId entityId, List<? extends KvEntry> telemetry) {
if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) {
List<CalculatedFieldLink> cfLinks = calculatedFieldService.findAllCalculatedFieldLinksByEntityId(tenantId, entityId);
if (!cfLinks.isEmpty()) {
cfLinks.forEach(link -> {
CalculatedFieldId calculatedFieldId = link.getCalculatedFieldId();
Map<String, String> attributes = link.getConfiguration().getAttributes();
Map<String, String> timeSeries = link.getConfiguration().getTimeSeries();
List<? extends KvEntry> filteredTelemetry = 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());
}
if (!updatedTelemetry.isEmpty()) {
calculatedFieldExecutionService.onTelemetryUpdate(tenantId, calculatedFieldId, updatedTelemetry);
}
});
}
}
}
private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts) {
if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) {
Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId),
@ -263,6 +318,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
ListenableFuture<List<Long>> saveFuture = attrService.save(tenantId, entityId, scope, attributes);
addVoidCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice));
updateTelemetryInCalculatedFields(tenantId, entityId, attributes);
}
@Override
@ -270,6 +326,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
ListenableFuture<List<Long>> saveFuture = attrService.save(tenantId, entityId, scope, attributes);
addVoidCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope.name(), attributes, notifyDevice));
updateTelemetryInCalculatedFields(tenantId, entityId, attributes);
}
@Override
@ -283,6 +340,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
ListenableFuture<List<Long>> saveFuture = tsService.saveLatest(tenantId, entityId, ts);
addVoidCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts));
updateTelemetryInCalculatedFields(tenantId, entityId, ts);
}
@Override

9
application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java

@ -21,11 +21,12 @@ import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.security.Authority;
@ -144,7 +145,7 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
config.setArguments(Map.of("T", argument));
SimpleCalculatedFieldConfiguration.Output output = new SimpleCalculatedFieldConfiguration.Output();
Output output = new Output();
output.setType("TIME_SERIES");
output.setExpression("T - (100 - H) / 5");

4
common/dao-api/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldService.java

@ -54,12 +54,16 @@ public interface CalculatedFieldService extends EntityDaoService {
List<CalculatedFieldLink> findAllCalculatedFieldLinksById(TenantId tenantId, CalculatedFieldId calculatedFieldId);
List<CalculatedFieldLink> findAllCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId);
ListenableFuture<List<CalculatedFieldLink>> findAllCalculatedFieldLinksByIdAsync(TenantId tenantId, CalculatedFieldId calculatedFieldId);
PageData<CalculatedFieldLink> findAllCalculatedFieldLinks(PageLink pageLink);
boolean referencedInAnyCalculatedField(TenantId tenantId, EntityId referencedEntityId);
boolean referencedInAnyCalculatedFieldIncludingEntityId(TenantId tenantId, EntityId referencedEntityId);
boolean existsCalculatedFieldByEntityId(TenantId tenantId, EntityId entityId);
}

2
common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedField.java

@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;

8
common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldLinkConfiguration.java

@ -17,13 +17,13 @@ package org.thingsboard.server.common.data.cf;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
@Data
public class CalculatedFieldLinkConfiguration {
private List<String> attributes = new ArrayList<>();
private List<String> timeSeries = new ArrayList<>();
private Map<String, String> attributes = new HashMap<>();
private Map<String, String> timeSeries = new HashMap<>();
}

4
common/data/src/main/java/org/thingsboard/server/common/data/cf/Argument.java → common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Argument.java

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.cf;
package org.thingsboard.server.common.data.cf.configuration;
import lombok.Data;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId;
@Data
@ -24,6 +25,7 @@ public class Argument {
private EntityId entityId;
private String key;
private String type;
private AttributeScope scope;
private String defaultValue;
private int limit;

43
common/data/src/main/java/org/thingsboard/server/common/data/cf/BaseCalculatedFieldConfiguration.java → common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/BaseCalculatedFieldConfiguration.java

@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.cf;
package org.thingsboard.server.common.data.cf.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedFieldLinkConfiguration;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -60,18 +62,20 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
@Override
public CalculatedFieldLinkConfiguration getReferencedEntityConfig(EntityId entityId) {
CalculatedFieldLinkConfiguration linkConfiguration = new CalculatedFieldLinkConfiguration();
arguments.values().stream()
.filter(argument -> argument.getEntityId().equals(entityId))
.forEach(argument -> {
switch (argument.getType()) {
case "ATTRIBUTES":
linkConfiguration.getAttributes().add(argument.getKey());
break;
case "TIME_SERIES":
linkConfiguration.getTimeSeries().add(argument.getKey());
break;
}
});
for (Map.Entry<String, Argument> entry : arguments.entrySet()) {
Argument argument = entry.getValue();
if (argument.getEntityId().equals(entityId)) {
switch (argument.getType()) {
case "ATTRIBUTES":
linkConfiguration.getAttributes().put(entry.getKey(), argument.getKey());
break;
case "TIME_SERIES":
linkConfiguration.getTimeSeries().put(entry.getKey(), argument.getKey());
break;
}
}
}
return linkConfiguration;
}
@ -93,25 +97,21 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
}
argumentNode.put("key", argument.getKey());
argumentNode.put("type", argument.getType());
argumentNode.put("scope", String.valueOf(argument.getScope()));
argumentNode.put("defaultValue", argument.getDefaultValue());
});
if (output != null) {
ObjectNode outputNode = configNode.putObject("output");
outputNode.put("name", output.getName());
outputNode.put("type", output.getType());
outputNode.put("scope", String.valueOf(output.getScope()));
outputNode.put("expression", output.getExpression());
}
return configNode;
}
@Data
public static class Output {
private String name;
private String type;
private String expression;
}
private BaseCalculatedFieldConfiguration toCalculatedFieldConfig(JsonNode config, EntityType entityType, UUID entityId) {
if (config == null || !config.isObject()) {
return null;
@ -133,6 +133,7 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
}
argument.setKey(argumentNode.get("key").asText());
argument.setType(argumentNode.get("type").asText());
argument.setScope(AttributeScope.valueOf(argumentNode.get("scope").asText()));
argument.setDefaultValue(argumentNode.get("defaultValue").asText());
arguments.put(key, argument);
});
@ -142,7 +143,9 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
JsonNode outputNode = config.get("output");
if (outputNode != null) {
Output output = new Output();
output.setName(outputNode.get("name").asText());
output.setType(outputNode.get("type").asText());
output.setScope(AttributeScope.valueOf(outputNode.get("scope").asText()));
output.setExpression(outputNode.get("expression").asText());
this.setOutput(output);
}

9
common/data/src/main/java/org/thingsboard/server/common/data/cf/CalculatedFieldConfiguration.java → common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/CalculatedFieldConfiguration.java

@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.cf;
package org.thingsboard.server.common.data.cf.configuration;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedFieldLinkConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.List;
@ -32,7 +34,8 @@ import java.util.UUID;
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE")
@JsonSubTypes.Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE"),
@JsonSubTypes.Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT")
})
public interface CalculatedFieldConfiguration {
@ -41,7 +44,7 @@ public interface CalculatedFieldConfiguration {
Map<String, Argument> getArguments();
BaseCalculatedFieldConfiguration.Output getOutput();
Output getOutput();
@JsonIgnore
List<EntityId> getReferencedEntities();

29
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/Output.java

@ -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.common.data.cf.configuration;
import lombok.Data;
import org.thingsboard.server.common.data.AttributeScope;
@Data
public class Output {
private String name;
private String type;
private AttributeScope scope;
private String expression;
}

40
common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/ScriptCalculatedFieldConfiguration.java

@ -0,0 +1,40 @@
/**
* 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.common.data.cf.configuration;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import java.util.UUID;
@Data
public class ScriptCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration {
public ScriptCalculatedFieldConfiguration() {
super();
}
public ScriptCalculatedFieldConfiguration(JsonNode config, EntityType entityType, UUID entityId) {
super(config, entityType, entityId);
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.SCRIPT;
}
}

3
common/data/src/main/java/org/thingsboard/server/common/data/cf/SimpleCalculatedFieldConfiguration.java → common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/SimpleCalculatedFieldConfiguration.java

@ -13,11 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.cf;
package org.thingsboard.server.common.data.cf.configuration;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import java.util.UUID;

16
dao/src/main/java/org/thingsboard/server/dao/cf/BaseCalculatedFieldService.java

@ -21,7 +21,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.CalculatedFieldLinkId;
@ -173,6 +173,12 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
return calculatedFieldLinkDao.findCalculatedFieldLinksByCalculatedFieldId(tenantId, calculatedFieldId);
}
@Override
public List<CalculatedFieldLink> findAllCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId) {
log.trace("Executing findAllCalculatedFieldLinksByEntityId, entityId [{}]", entityId);
return calculatedFieldLinkDao.findCalculatedFieldLinksByEntityId(tenantId, entityId);
}
@Override
public ListenableFuture<List<CalculatedFieldLink>> findAllCalculatedFieldLinksByIdAsync(TenantId tenantId, CalculatedFieldId calculatedFieldId) {
log.trace("Executing findAllCalculatedFieldLinksByIdAsync, calculatedFieldId [{}]", calculatedFieldId);
@ -195,6 +201,14 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
.anyMatch(referencedEntities -> referencedEntities.contains(referencedEntityId));
}
@Override
public boolean referencedInAnyCalculatedFieldIncludingEntityId(TenantId tenantId, EntityId referencedEntityId) {
return calculatedFieldDao.findAllByTenantId(tenantId).stream()
.map(CalculatedField::getConfiguration)
.map(CalculatedFieldConfiguration::getReferencedEntities)
.anyMatch(referencedEntities -> referencedEntities.contains(referencedEntityId));
}
@Override
public boolean existsCalculatedFieldByEntityId(TenantId tenantId, EntityId entityId) {
return calculatedFieldDao.existsByEntityId(tenantId, entityId);

3
dao/src/main/java/org/thingsboard/server/dao/cf/CalculatedFieldLinkDao.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.cf;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -29,6 +30,8 @@ public interface CalculatedFieldLinkDao extends Dao<CalculatedFieldLink> {
List<CalculatedFieldLink> findCalculatedFieldLinksByCalculatedFieldId(TenantId tenantId, CalculatedFieldId calculatedFieldId);
List<CalculatedFieldLink> findCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId);
ListenableFuture<List<CalculatedFieldLink>> findCalculatedFieldLinksByCalculatedFieldIdAsync(TenantId tenantId, CalculatedFieldId calculatedFieldId);
List<CalculatedFieldLink> findAll();

7
dao/src/main/java/org/thingsboard/server/dao/model/sql/CalculatedFieldEntity.java

@ -24,9 +24,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
@ -122,6 +123,8 @@ public class CalculatedFieldEntity extends BaseSqlEntity<CalculatedField> implem
switch (CalculatedFieldType.valueOf(type)) {
case SIMPLE:
return new SimpleCalculatedFieldConfiguration(config, entityType, entityId);
case SCRIPT:
return new ScriptCalculatedFieldConfiguration(config, entityType, entityId);
default:
throw new IllegalArgumentException("Unsupported calculated field type: " + type + "!");
}

2
dao/src/main/java/org/thingsboard/server/dao/sql/cf/CalculatedFieldLinkRepository.java

@ -25,4 +25,6 @@ public interface CalculatedFieldLinkRepository extends JpaRepository<CalculatedF
List<CalculatedFieldLinkEntity> findAllByTenantIdAndCalculatedFieldId(UUID tenantId, UUID calculatedFieldId);
List<CalculatedFieldLinkEntity> findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId);
}

4
dao/src/main/java/org/thingsboard/server/dao/sql/cf/DefaultNativeCalculatedFieldRepository.java

@ -25,11 +25,11 @@ import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.cf.CalculatedFieldLinkConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.CalculatedFieldLinkId;
import org.thingsboard.server.common.data.id.EntityIdFactory;

6
dao/src/main/java/org/thingsboard/server/dao/sql/cf/JpaCalculatedFieldLinkDao.java

@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -49,6 +50,11 @@ public class JpaCalculatedFieldLinkDao extends JpaAbstractDao<CalculatedFieldLin
return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAllByTenantIdAndCalculatedFieldId(tenantId.getId(), calculatedFieldId.getId()));
}
@Override
public List<CalculatedFieldLink> findCalculatedFieldLinksByEntityId(TenantId tenantId, EntityId entityId) {
return DaoUtil.convertDataList(calculatedFieldLinkRepository.findAllByTenantIdAndEntityId(tenantId.getId(), entityId.getId()));
}
@Override
public ListenableFuture<List<CalculatedFieldLink>> findCalculatedFieldLinksByCalculatedFieldIdAsync(TenantId tenantId, CalculatedFieldId calculatedFieldId) {
return service.submit(() -> findCalculatedFieldLinksByCalculatedFieldId(tenantId, calculatedFieldId));

7
dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java

@ -30,10 +30,11 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -888,7 +889,7 @@ public class AssetServiceTest extends AbstractServiceTest {
config.setArguments(Map.of("T", argument));
SimpleCalculatedFieldConfiguration.Output output = new SimpleCalculatedFieldConfiguration.Output();
Output output = new Output();
output.setType("TIME_SERIES");
output.setExpression("T - (100 - H) / 5");

9
dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java

@ -23,11 +23,12 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
@ -157,7 +158,7 @@ public class CalculatedFieldServiceTest extends AbstractServiceTest {
config.setArguments(Map.of("T", argument));
SimpleCalculatedFieldConfiguration.Output output = new SimpleCalculatedFieldConfiguration.Output();
Output output = new Output();
output.setType("TIME_SERIES");
output.setExpression("T - (100 - H) / 5");

7
dao/src/test/java/org/thingsboard/server/dao/service/CustomerServiceTest.java

@ -31,10 +31,11 @@ import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -383,7 +384,7 @@ public class CustomerServiceTest extends AbstractServiceTest {
config.setArguments(Map.of("T", argument));
SimpleCalculatedFieldConfiguration.Output output = new SimpleCalculatedFieldConfiguration.Output();
Output output = new Output();
output.setType("TIME_SERIES");
output.setExpression("T - (100 - H) / 5");

7
dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java

@ -39,10 +39,11 @@ import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.cf.Argument;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Output;
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
@ -1226,7 +1227,7 @@ public class DeviceServiceTest extends AbstractServiceTest {
config.setArguments(Map.of("T", argument));
SimpleCalculatedFieldConfiguration.Output output = new SimpleCalculatedFieldConfiguration.Output();
Output output = new Output();
output.setType("TIME_SERIES");
output.setExpression("T - (100 - H) / 5");

Loading…
Cancel
Save