added last records type of cf

This commit is contained in:
IrynaMatveieva 2024-11-25 17:28:44 +02:00
parent 2c7c6f0c5e
commit c6d91c4ce8
14 changed files with 327 additions and 52 deletions

View File

@ -48,12 +48,16 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.msg.TbMsg;
@ -69,7 +73,10 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldCtx;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldCtxId;
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.state.CalculationContext;
import org.thingsboard.server.service.cf.ctx.state.LastRecordsCalculatedFieldState;
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;
@ -84,6 +91,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.SCOPE;
@ -109,6 +117,8 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
private final ConcurrentMap<CalculatedFieldId, List<CalculatedFieldLink>> calculatedFieldLinks = new ConcurrentHashMap<>();
private final ConcurrentMap<CalculatedFieldCtxId, CalculatedFieldCtx> states = new ConcurrentHashMap<>();
private static final int MAX_LAST_RECORDS_VALUE = 1024;
@Value("${calculatedField.initFetchPackSize:50000}")
@Getter
private int initFetchPackSize;
@ -215,7 +225,19 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
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);
Map<String, ArgumentEntry> argumentValues = updatedTelemetry.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> {
ArgumentEntry argumentEntry = new ArgumentEntry();
argumentEntry.setKvEntry(entry.getValue());
if (entry.getValue() instanceof TsKvEntry) {
argumentEntry.setKvEntries(List.of((TsKvEntry) entry.getValue()));
}
return argumentEntry;
}
));
updateOrInitializeState(calculatedField, calculatedField.getEntityId(), argumentValues);
log.info("Successfully updated time series for calculatedFieldId: [{}]", calculatedFieldId);
} catch (Exception e) {
log.trace("Failed to update telemetry for calculatedFieldId: [{}]", calculatedFieldId, e);
@ -308,12 +330,12 @@ 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, KvEntry> argumentValues = new HashMap<>();
Map<String, ArgumentEntry> argumentValues = new HashMap<>();
AtomicInteger remaining = new AtomicInteger(arguments.size());
arguments.forEach((key, argument) -> Futures.addCallback(fetchArgumentValue(tenantId, argument, entityId), new FutureCallback<>() {
arguments.forEach((key, argument) -> Futures.addCallback(fetchArgumentValue(calculatedField, argument), new FutureCallback<>() {
@Override
public void onSuccess(Optional<? extends KvEntry> result) {
argumentValues.put(key, result.orElse(null));
public void onSuccess(ArgumentEntry result) {
argumentValues.put(key, result);
if (remaining.decrementAndGet() == 0) {
updateOrInitializeState(calculatedField, entityId, argumentValues);
}
@ -327,10 +349,37 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
}, calculatedFieldCallbackExecutor));
}
private ListenableFuture<Optional<? extends KvEntry>> fetchArgumentValue(TenantId tenantId, Argument argument, EntityId targetEntityId) {
private ListenableFuture<ArgumentEntry> fetchArgumentValue(CalculatedField calculatedField, Argument argument) {
TenantId tenantId = calculatedField.getTenantId();
EntityId cfEntityId = calculatedField.getEntityId();
EntityId argumentEntityId = argument.getEntityId();
EntityId entityId = EntityType.DEVICE_PROFILE.equals(argumentEntityId.getEntityType()) || EntityType.ASSET_PROFILE.equals(argumentEntityId.getEntityType()) ? targetEntityId : argumentEntityId;
return switch (argument.getType()) {
EntityId entityId = EntityType.DEVICE_PROFILE.equals(argumentEntityId.getEntityType()) || EntityType.ASSET_PROFILE.equals(argumentEntityId.getEntityType())
? cfEntityId
: argumentEntityId;
if (CalculatedFieldType.LAST_RECORDS.equals(calculatedField.getType())) {
return fetchLastRecords(tenantId, entityId, argument);
}
return fetchKvEntry(tenantId, entityId, argument);
}
private ListenableFuture<ArgumentEntry> fetchLastRecords(TenantId tenantId, EntityId entityId, Argument argument) {
long startTs = Math.max(argument.getStartTs(), 0);
long timeWindow = argument.getTimeWindow() == 0 ? System.currentTimeMillis() : argument.getTimeWindow();
long endTs = startTs + timeWindow;
int limit = argument.getLimit() == 0 ? MAX_LAST_RECORDS_VALUE : argument.getLimit();
ReadTsKvQuery query = new BaseReadTsKvQuery(argument.getKey(), startTs, endTs, 0, limit, Aggregation.NONE);
ListenableFuture<List<TsKvEntry>> lastRecordsFuture = timeseriesService.findAll(tenantId, entityId, List.of(query));
return Futures.transform(lastRecordsFuture, lastRecords -> {
ArgumentEntry argumentEntry = new ArgumentEntry();
argumentEntry.setKvEntries(lastRecords);
return argumentEntry;
}, calculatedFieldExecutor);
}
private ListenableFuture<ArgumentEntry> fetchKvEntry(TenantId tenantId, EntityId entityId, Argument argument) {
ListenableFuture<Optional<? extends KvEntry>> kvEntryFuture = switch (argument.getType()) {
case "ATTRIBUTES" -> Futures.transform(
attributesService.find(tenantId, entityId, argument.getScope(), argument.getKey()),
result -> result.or(() -> Optional.of(
@ -342,9 +391,16 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
result -> result.or(() -> Optional.of(
new BasicTsKvEntry(System.currentTimeMillis(), createDefaultKvEntry(argument))
)),
MoreExecutors.directExecutor());
calculatedFieldExecutor);
default -> throw new IllegalArgumentException("Invalid argument type '" + argument.getType() + "'.");
};
return Futures.transform(kvEntryFuture, kvEntry -> {
ArgumentEntry argumentEntry = new ArgumentEntry();
if (kvEntry.isPresent()) {
argumentEntry.setKvEntry(kvEntry.orElse(null));
}
return argumentEntry;
}, calculatedFieldExecutor);
}
private KvEntry createDefaultKvEntry(Argument argument) {
@ -359,7 +415,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
return new StringDataEntry(key, defaultValue);
}
private void updateOrInitializeState(CalculatedField calculatedField, EntityId entityId, Map<String, KvEntry> argumentValues) {
private void updateOrInitializeState(CalculatedField calculatedField, EntityId entityId, Map<String, ArgumentEntry> argumentValues) {
CalculatedFieldCtxId ctxId = new CalculatedFieldCtxId(calculatedField.getUuidId(), entityId.getId());
CalculatedFieldCtx calculatedFieldCtx = states.computeIfAbsent(ctxId, ctx -> new CalculatedFieldCtx(ctxId, null));
@ -373,7 +429,12 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
states.put(ctxId, calculatedFieldCtx);
rocksDBService.put(JacksonUtil.writeValueAsString(ctxId), JacksonUtil.writeValueAsString(calculatedFieldCtx));
ListenableFuture<CalculatedFieldResult> resultFuture = state.performCalculation(calculatedField.getTenantId(), calculatedField.getConfiguration(), tbelInvokeService);
CalculationContext ctx = CalculationContext.builder()
.tenantId(calculatedField.getTenantId())
.configuration(calculatedField.getConfiguration())
.tbelInvokeService(tbelInvokeService)
.build();
ListenableFuture<CalculatedFieldResult> resultFuture = state.performCalculation(ctx);
Futures.addCallback(resultFuture, new FutureCallback<>() {
@Override
public void onSuccess(CalculatedFieldResult result) {
@ -414,6 +475,7 @@ public class DefaultCalculatedFieldExecutionService extends AbstractPartitionBas
return switch (calculatedFieldType) {
case SIMPLE -> new SimpleCalculatedFieldState();
case SCRIPT -> new ScriptCalculatedFieldState();
case LAST_RECORDS -> new LastRecordsCalculatedFieldState();
};
}

View File

@ -0,0 +1,30 @@
/**
* 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 lombok.Data;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import java.util.List;
@Data
public class ArgumentEntry {
private KvEntry kvEntry;
private List<TsKvEntry> kvEntries;
}

View File

@ -19,11 +19,8 @@ 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.id.TenantId;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
@ -36,7 +33,8 @@ import java.util.Map;
)
@JsonSubTypes({
@JsonSubTypes.Type(value = SimpleCalculatedFieldState.class, name = "SIMPLE"),
@JsonSubTypes.Type(value = ScriptCalculatedFieldState.class, name = "SCRIPT")
@JsonSubTypes.Type(value = ScriptCalculatedFieldState.class, name = "SCRIPT"),
@JsonSubTypes.Type(value = LastRecordsCalculatedFieldState.class, name = "LAST_RECORDS")
})
public interface CalculatedFieldState {
@ -47,8 +45,8 @@ public interface CalculatedFieldState {
return argumentValues.keySet().containsAll(arguments.keySet());
}
void initState(Map<String, KvEntry> argumentValues);
void initState(Map<String, ArgumentEntry> argumentValues);
ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService);
ListenableFuture<CalculatedFieldResult> performCalculation(CalculationContext ctx);
}

View File

@ -0,0 +1,35 @@
/**
* 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 lombok.Builder;
import lombok.Data;
import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.KvEntry;
import java.util.Map;
@Data
@Builder
public class CalculationContext {
private TenantId tenantId;
private CalculatedFieldConfiguration configuration;
private TbelInvokeService tbelInvokeService;
}

View File

@ -0,0 +1,91 @@
/**
* 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 lombok.Data;
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.configuration.Output;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Data
public class LastRecordsCalculatedFieldState implements CalculatedFieldState {
private Map<String, List<TsKvEntry>> arguments;
public LastRecordsCalculatedFieldState() {
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.LAST_RECORDS;
}
@Override
public void initState(Map<String, ArgumentEntry> argumentValues) {
if (arguments == null) {
arguments = new HashMap<>();
}
argumentValues.forEach((key, argumentEntry) -> {
List<TsKvEntry> tsKvEntryList = arguments.computeIfAbsent(key, k -> new ArrayList<>());
tsKvEntryList.addAll(argumentEntry.getKvEntries());
});
}
@Override
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculationContext ctx) {
CalculatedFieldConfiguration configuration = ctx.getConfiguration();
Map<String, Argument> configArguments = configuration.getArguments();
Output output = configuration.getOutput();
Map<String, Object> resultMap = new HashMap<>();
arguments.replaceAll((key, entries) -> {
int limit = configArguments.get(key).getLimit();
List<TsKvEntry> limitedEntries = entries.stream()
.sorted(Comparator.comparingLong(TsKvEntry::getTs).reversed())
.limit(limit)
.collect(Collectors.toList());
Map<Long, Object> valueWithTs = limitedEntries.stream()
.collect(Collectors.toMap(TsKvEntry::getTs, TsKvEntry::getValue));
resultMap.put(key, valueWithTs);
return limitedEntries;
});
CalculatedFieldResult calculatedFieldResult = new CalculatedFieldResult();
calculatedFieldResult.setType(output.getType());
calculatedFieldResult.setScope(output.getScope());
calculatedFieldResult.setResultMap(resultMap);
return Futures.immediateFuture(calculatedFieldResult);
}
}

View File

@ -40,7 +40,7 @@ public class ScriptCalculatedFieldState implements CalculatedFieldState {
@JsonIgnore
private CalculatedFieldScriptEngine calculatedFieldScriptEngine;
private Map<String, KvEntry> arguments = new HashMap<>();
private Map<String, KvEntry> arguments;
public ScriptCalculatedFieldState() {
}
@ -50,22 +50,27 @@ public class ScriptCalculatedFieldState implements CalculatedFieldState {
return CalculatedFieldType.SCRIPT;
}
@Override
public void initState(Map<String, KvEntry> argumentValues) {
if (arguments == null) {
this.arguments = new HashMap<>();
}
this.arguments.putAll(argumentValues);
}
@Override
public ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService) {
public void initState(Map<String, ArgumentEntry> argumentValues) {
if (arguments == null) {
arguments = new HashMap<>();
}
argumentValues.forEach((key, value) -> arguments.put(key, value.getKvEntry()));
}
@Override
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculationContext ctx) {
CalculatedFieldConfiguration calculatedFieldConfiguration = ctx.getConfiguration();
TbelInvokeService tbelInvokeService = ctx.getTbelInvokeService();
if (tbelInvokeService == null) {
throw new IllegalArgumentException("TBEL script engine is disabled!");
}
if (calculatedFieldScriptEngine == null) {
initEngine(tenantId, calculatedFieldConfiguration, tbelInvokeService);
initEngine(ctx.getTenantId(), calculatedFieldConfiguration, tbelInvokeService);
}
ListenableFuture<Object> resultFuture = calculatedFieldScriptEngine.executeScriptAsync(arguments);

View File

@ -20,12 +20,10 @@ 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.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;
@ -37,21 +35,26 @@ public class SimpleCalculatedFieldState implements CalculatedFieldState {
private Map<String, KvEntry> arguments;
public SimpleCalculatedFieldState() {
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.SIMPLE;
}
@Override
public void initState(Map<String, KvEntry> argumentValues) {
public void initState(Map<String, ArgumentEntry> argumentValues) {
if (arguments == null) {
arguments = new HashMap<>();
}
arguments.putAll(argumentValues);
argumentValues.forEach((key, value) -> arguments.put(key, value.getKvEntry()));
}
@Override
public ListenableFuture<CalculatedFieldResult> performCalculation(TenantId tenantId, CalculatedFieldConfiguration calculatedFieldConfiguration, TbelInvokeService tbelInvokeService) {
public ListenableFuture<CalculatedFieldResult> performCalculation(CalculationContext ctx) {
CalculatedFieldConfiguration calculatedFieldConfiguration = ctx.getConfiguration();
Output output = calculatedFieldConfiguration.getOutput();
Map<String, Argument> arguments = calculatedFieldConfiguration.getArguments();

View File

@ -17,6 +17,6 @@ package org.thingsboard.server.common.data.cf;
public enum CalculatedFieldType {
SIMPLE, SCRIPT
SIMPLE, SCRIPT, LAST_RECORDS
}

View File

@ -29,6 +29,7 @@ public class Argument {
private String defaultValue;
private int limit;
private long startTs;
private long timeWindow;
}

View File

@ -101,6 +101,9 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
argumentNode.put("type", argument.getType());
argumentNode.put("scope", String.valueOf(argument.getScope()));
argumentNode.put("defaultValue", argument.getDefaultValue());
argumentNode.put("limit", String.valueOf(argument.getLimit()));
argumentNode.put("startTs", String.valueOf(argument.getStartTs()));
argumentNode.put("timeWindow", String.valueOf(argument.getTimeWindow()));
});
if (expression != null) {
@ -144,7 +147,18 @@ public abstract class BaseCalculatedFieldConfiguration implements CalculatedFiel
if (scope != null && !scope.isNull() && !scope.asText().equals("null")) {
argument.setScope(AttributeScope.valueOf(scope.asText()));
}
argument.setDefaultValue(argumentNode.get("defaultValue").asText());
if (argumentNode.hasNonNull("defaultValue")) {
argument.setDefaultValue(argumentNode.get("defaultValue").asText());
}
if (argumentNode.hasNonNull("limit")) {
argument.setLimit(argumentNode.get("limit").asInt());
}
if (argumentNode.hasNonNull("startTs")) {
argument.setStartTs(argumentNode.get("startTs").asLong());
}
if (argumentNode.hasNonNull("timeWindow")) {
argument.setTimeWindow(argumentNode.get("timeWindow").asInt());
}
arguments.put(key, argument);
});
}

View File

@ -35,7 +35,8 @@ import java.util.UUID;
)
@JsonSubTypes({
@JsonSubTypes.Type(value = SimpleCalculatedFieldConfiguration.class, name = "SIMPLE"),
@JsonSubTypes.Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT")
@JsonSubTypes.Type(value = ScriptCalculatedFieldConfiguration.class, name = "SCRIPT"),
@JsonSubTypes.Type(value = LastRecordsCalculatedFieldConfiguration.class, name = "LAST_RECORDS")
})
public interface CalculatedFieldConfiguration {

View File

@ -0,0 +1,39 @@
/**
* 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 LastRecordsCalculatedFieldConfiguration extends BaseCalculatedFieldConfiguration implements CalculatedFieldConfiguration {
public LastRecordsCalculatedFieldConfiguration() {
}
public LastRecordsCalculatedFieldConfiguration(JsonNode config, EntityType entityType, UUID entityId) {
super(config, entityType, entityId);
}
@Override
public CalculatedFieldType getType() {
return CalculatedFieldType.LAST_RECORDS;
}
}

View File

@ -24,8 +24,9 @@ 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.configuration.CalculatedFieldConfiguration;
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.LastRecordsCalculatedFieldConfiguration;
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;
@ -120,14 +121,11 @@ public class CalculatedFieldEntity extends BaseSqlEntity<CalculatedField> implem
}
private CalculatedFieldConfiguration readCalculatedFieldConfiguration(JsonNode config, EntityType entityType, UUID entityId) {
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 + "!");
}
return switch (CalculatedFieldType.valueOf(type)) {
case SIMPLE -> new SimpleCalculatedFieldConfiguration(config, entityType, entityId);
case SCRIPT -> new ScriptCalculatedFieldConfiguration(config, entityType, entityId);
case LAST_RECORDS -> new LastRecordsCalculatedFieldConfiguration(config, entityType, entityId);
};
}
}

View File

@ -29,6 +29,7 @@ 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.configuration.CalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.LastRecordsCalculatedFieldConfiguration;
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;
@ -136,14 +137,11 @@ public class DefaultNativeCalculatedFieldRepository implements NativeCalculatedF
}
private CalculatedFieldConfiguration readCalculatedFieldConfiguration(CalculatedFieldType type, JsonNode config, EntityType entityType, UUID entityId) {
switch (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 + "!");
}
return switch (type) {
case SIMPLE -> new SimpleCalculatedFieldConfiguration(config, entityType, entityId);
case SCRIPT -> new ScriptCalculatedFieldConfiguration(config, entityType, entityId);
case LAST_RECORDS -> new LastRecordsCalculatedFieldConfiguration(config, entityType, entityId);
};
}
}