removed no needed logic from geofencing arugment

This commit is contained in:
dshvaika 2025-08-08 17:28:57 +03:00
parent fb84eb0695
commit 409328dbe3
8 changed files with 33 additions and 87 deletions

View File

@ -36,8 +36,6 @@ 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.Argument;
import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
import org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType; import org.thingsboard.server.common.data.cf.configuration.CFArgumentDynamicSourceType;
import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
import org.thingsboard.server.common.data.cf.configuration.OutputType; import org.thingsboard.server.common.data.cf.configuration.OutputType;
import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration; import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.CalculatedFieldId;
@ -131,18 +129,18 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
@Override @Override
public ListenableFuture<CalculatedFieldState> fetchStateFromDb(CalculatedFieldCtx ctx, EntityId entityId) { public ListenableFuture<CalculatedFieldState> fetchStateFromDb(CalculatedFieldCtx ctx, EntityId entityId) {
Map<String, ListenableFuture<ArgumentEntry>> argFutures = new HashMap<>(); Map<String, ListenableFuture<ArgumentEntry>> argFutures = switch (ctx.getCalculatedField().getType()) {
case GEOFENCING -> fetchGeofencingCalculatedFieldArguments(ctx, entityId, false);
if (ctx.getCalculatedField().getType().equals(CalculatedFieldType.GEOFENCING)) { case SIMPLE, SCRIPT -> {
fetchGeofencingCalculatedFieldArguments(ctx, entityId, argFutures, false); Map<String, ListenableFuture<ArgumentEntry>> futures = new HashMap<>();
} else {
for (var entry : ctx.getArguments().entrySet()) { for (var entry : ctx.getArguments().entrySet()) {
var argEntityId = resolveEntityId(entityId, entry); var argEntityId = resolveEntityId(entityId, entry);
var argValueFuture = fetchKvEntry(ctx.getTenantId(), argEntityId, entry.getValue()); var argValueFuture = fetchKvEntry(ctx.getTenantId(), argEntityId, entry.getValue());
argFutures.put(entry.getKey(), argValueFuture); futures.put(entry.getKey(), argValueFuture);
} }
yield futures;
} }
};
return Futures.whenAllComplete(argFutures.values()).call(() -> { return Futures.whenAllComplete(argFutures.values()).call(() -> {
var result = createStateByType(ctx); var result = createStateByType(ctx);
result.updateState(ctx, resolveArgumentFutures(argFutures)); result.updateState(ctx, resolveArgumentFutures(argFutures));
@ -156,14 +154,11 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
if (!ctx.getCalculatedField().getType().equals(CalculatedFieldType.GEOFENCING)) { if (!ctx.getCalculatedField().getType().equals(CalculatedFieldType.GEOFENCING)) {
return Map.of(); return Map.of();
} }
Map<String, ListenableFuture<ArgumentEntry>> argFutures = new HashMap<>(); return resolveArgumentFutures(fetchGeofencingCalculatedFieldArguments(ctx, entityId, true));
fetchGeofencingCalculatedFieldArguments(ctx, entityId, argFutures, true);
return resolveArgumentFutures(argFutures);
} }
private void fetchGeofencingCalculatedFieldArguments(CalculatedFieldCtx ctx, EntityId entityId, Map<String, ListenableFuture<ArgumentEntry>> argFutures, boolean dynamicArgumentsOnly) { private Map<String, ListenableFuture<ArgumentEntry>> fetchGeofencingCalculatedFieldArguments(CalculatedFieldCtx ctx, EntityId entityId, boolean dynamicArgumentsOnly) {
var configuration = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); Map<String, ListenableFuture<ArgumentEntry>> argFutures = new HashMap<>();
var zoneGroupConfigs = configuration.getGeofencingZoneGroupConfigurations();
Set<Entry<String, Argument>> entries = ctx.getArguments().entrySet(); Set<Entry<String, Argument>> entries = ctx.getArguments().entrySet();
if (dynamicArgumentsOnly) { if (dynamicArgumentsOnly) {
entries = entries.stream() entries = entries.stream()
@ -175,13 +170,13 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
case ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY -> case ENTITY_ID_LATITUDE_ARGUMENT_KEY, ENTITY_ID_LONGITUDE_ARGUMENT_KEY ->
argFutures.put(entry.getKey(), fetchKvEntry(ctx.getTenantId(), resolveEntityId(entityId, entry), entry.getValue())); argFutures.put(entry.getKey(), fetchKvEntry(ctx.getTenantId(), resolveEntityId(entityId, entry), entry.getValue()));
default -> { default -> {
var zoneGroupConfiguration = zoneGroupConfigs.get(entry.getKey());
var resolvedEntityIdsFuture = resolveGeofencingEntityIds(ctx.getTenantId(), entityId, entry); var resolvedEntityIdsFuture = resolveGeofencingEntityIds(ctx.getTenantId(), entityId, entry);
argFutures.put(entry.getKey(), Futures.transformAsync(resolvedEntityIdsFuture, resolvedEntityIds -> argFutures.put(entry.getKey(), Futures.transformAsync(resolvedEntityIdsFuture, resolvedEntityIds ->
fetchGeofencingKvEntry(ctx.getTenantId(), resolvedEntityIds, entry.getValue(), zoneGroupConfiguration), calculatedFieldCallbackExecutor)); fetchGeofencingKvEntry(ctx.getTenantId(), resolvedEntityIds, entry.getValue()), calculatedFieldCallbackExecutor));
} }
} }
} }
return argFutures;
} }
@Override @Override
@ -321,12 +316,11 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
}; };
} }
private ListenableFuture<ArgumentEntry> fetchGeofencingKvEntry(TenantId tenantId, List<EntityId> geofencingEntities, private ListenableFuture<ArgumentEntry> fetchGeofencingKvEntry(TenantId tenantId, List<EntityId> geofencingEntities, Argument argument) {
Argument argument, GeofencingZoneGroupConfiguration zoneGroupConfiguration) {
if (argument.getRefEntityKey().getType() != ArgumentType.ATTRIBUTE) { if (argument.getRefEntityKey().getType() != ArgumentType.ATTRIBUTE) {
throw new IllegalStateException("Unsupported argument key type: " + argument.getRefEntityKey().getType()); throw new IllegalStateException("Unsupported argument key type: " + argument.getRefEntityKey().getType());
} }
List<ListenableFuture<Map.Entry<EntityId, AttributeKvEntry>>> kvFutures = geofencingEntities.stream() List<ListenableFuture<Entry<EntityId, AttributeKvEntry>>> kvFutures = geofencingEntities.stream()
.map(entityId -> { .map(entityId -> {
var attributesFuture = attributesService.find( var attributesFuture = attributesService.find(
tenantId, tenantId,
@ -341,10 +335,10 @@ public class DefaultCalculatedFieldProcessingService implements CalculatedFieldP
); );
}).collect(Collectors.toList()); }).collect(Collectors.toList());
ListenableFuture<List<Map.Entry<EntityId, AttributeKvEntry>>> allFutures = Futures.allAsList(kvFutures); ListenableFuture<List<Entry<EntityId, AttributeKvEntry>>> allFutures = Futures.allAsList(kvFutures);
return Futures.transform(allFutures, entries -> ArgumentEntry.createGeofencingValueArgument(entries.stream() return Futures.transform(allFutures, entries -> ArgumentEntry.createGeofencingValueArgument(entries.stream()
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)), zoneGroupConfiguration), .collect(Collectors.toMap(Entry::getKey, Entry::getValue))),
calculatedFieldCallbackExecutor calculatedFieldCallbackExecutor
); );
} }

View File

@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfArg;
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -62,8 +61,8 @@ public interface ArgumentEntry {
return new TsRollingArgumentEntry(kvEntries, limit, timeWindow); return new TsRollingArgumentEntry(kvEntries, limit, timeWindow);
} }
static ArgumentEntry createGeofencingValueArgument(Map<EntityId, KvEntry> entityIdkvEntryMap, GeofencingZoneGroupConfiguration zoneGroupConfiguration) { static ArgumentEntry createGeofencingValueArgument(Map<EntityId, KvEntry> entityIdkvEntryMap) {
return new GeofencingArgumentEntry(entityIdkvEntryMap, zoneGroupConfiguration); return new GeofencingArgumentEntry(entityIdkvEntryMap);
} }
} }

View File

@ -19,7 +19,6 @@ import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.script.api.tbel.TbelCfArg; import org.thingsboard.script.api.tbel.TbelCfArg;
import org.thingsboard.script.api.tbel.TbelCfTsGeofencingArg; import org.thingsboard.script.api.tbel.TbelCfTsGeofencingArg;
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.KvEntry;
@ -32,17 +31,14 @@ import java.util.stream.Collectors;
public class GeofencingArgumentEntry implements ArgumentEntry { public class GeofencingArgumentEntry implements ArgumentEntry {
private Map<EntityId, GeofencingZoneState> zoneStates; private Map<EntityId, GeofencingZoneState> zoneStates;
private GeofencingZoneGroupConfiguration zoneGroupConfiguration;
private boolean forceResetPrevious; private boolean forceResetPrevious;
public GeofencingArgumentEntry() { public GeofencingArgumentEntry() {
} }
public GeofencingArgumentEntry(Map<EntityId, KvEntry> entityIdkvEntryMap, public GeofencingArgumentEntry(Map<EntityId, KvEntry> entityIdkvEntryMap) {
GeofencingZoneGroupConfiguration zoneGroupConfiguration) {
this.zoneStates = toZones(entityIdkvEntryMap); this.zoneStates = toZones(entityIdkvEntryMap);
this.zoneGroupConfiguration = zoneGroupConfiguration;
} }
@Override @Override

View File

@ -23,7 +23,9 @@ import lombok.Data;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.geo.Coordinates; import org.thingsboard.common.util.geo.Coordinates;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.GeofencingCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent; import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
import org.thingsboard.server.service.cf.CalculatedFieldResult; import org.thingsboard.server.service.cf.CalculatedFieldResult;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId; import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId;
import org.thingsboard.server.utils.CalculatedFieldUtils; import org.thingsboard.server.utils.CalculatedFieldUtils;
@ -119,9 +121,12 @@ public class GeofencingCalculatedFieldState implements CalculatedFieldState {
double longitude = (double) arguments.get(ENTITY_ID_LONGITUDE_ARGUMENT_KEY).getValue(); double longitude = (double) arguments.get(ENTITY_ID_LONGITUDE_ARGUMENT_KEY).getValue();
Coordinates entityCoordinates = new Coordinates(latitude, longitude); Coordinates entityCoordinates = new Coordinates(latitude, longitude);
var configuration = (GeofencingCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration();
Map<String, GeofencingZoneGroupConfiguration> geofencingZoneGroupConfigurations = configuration.getGeofencingZoneGroupConfigurations();
ObjectNode resultNode = JacksonUtil.newObjectNode(); ObjectNode resultNode = JacksonUtil.newObjectNode();
getGeofencingArguments().forEach((argumentKey, argumentEntry) -> { getGeofencingArguments().forEach((argumentKey, argumentEntry) -> {
var zoneGroupConfig = argumentEntry.getZoneGroupConfiguration(); var zoneGroupConfig = geofencingZoneGroupConfigurations.get(argumentKey);
Set<GeofencingEvent> zoneEvents = argumentEntry.getZoneStates() Set<GeofencingEvent> zoneEvents = argumentEntry.getZoneStates()
.values() .values()
.stream() .stream()

View File

@ -18,8 +18,6 @@ package org.thingsboard.server.utils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.GeofencingEvent;
import org.thingsboard.server.common.data.cf.configuration.GeofencingZoneGroupConfiguration;
import org.thingsboard.server.common.data.id.CalculatedFieldId; import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -30,7 +28,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldEntit
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldIdProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldIdProto;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto; import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldStateProto;
import org.thingsboard.server.gen.transport.TransportProtos.GeofencingArgumentProto; import org.thingsboard.server.gen.transport.TransportProtos.GeofencingArgumentProto;
import org.thingsboard.server.gen.transport.TransportProtos.GeofencingEventProto;
import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneIdProto; import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneIdProto;
import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneProto; import org.thingsboard.server.gen.transport.TransportProtos.GeofencingZoneProto;
import org.thingsboard.server.gen.transport.TransportProtos.SingleValueArgumentProto; import org.thingsboard.server.gen.transport.TransportProtos.SingleValueArgumentProto;
@ -48,7 +45,6 @@ import org.thingsboard.server.service.cf.ctx.state.SimpleCalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry;
import org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry; import org.thingsboard.server.service.cf.ctx.state.TsRollingArgumentEntry;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.TreeMap; import java.util.TreeMap;
@ -127,15 +123,11 @@ public class CalculatedFieldUtils {
private static GeofencingArgumentProto toGeofencingArgumentProto(String argName, GeofencingArgumentEntry geofencingArgumentEntry) { private static GeofencingArgumentProto toGeofencingArgumentProto(String argName, GeofencingArgumentEntry geofencingArgumentEntry) {
var zoneGroupConfiguration = geofencingArgumentEntry.getZoneGroupConfiguration();
Map<EntityId, GeofencingZoneState> zoneStates = geofencingArgumentEntry.getZoneStates(); Map<EntityId, GeofencingZoneState> zoneStates = geofencingArgumentEntry.getZoneStates();
GeofencingArgumentProto.Builder builder = GeofencingArgumentProto.newBuilder() GeofencingArgumentProto.Builder builder = GeofencingArgumentProto.newBuilder()
.setArgName(argName) .setArgName(argName);
.setTelemetryPrefix(zoneGroupConfiguration.getReportTelemetryPrefix());
zoneStates.forEach((entityId, zoneState) -> zoneStates.forEach((entityId, zoneState) ->
builder.addZones(toGeofencingZoneProto(entityId, zoneState))); builder.addZones(toGeofencingZoneProto(entityId, zoneState)));
zoneGroupConfiguration.getReportEvents().forEach(event ->
builder.addReportEvents(GeofencingEventProto.forNumber(event.getProtoNumber())));
return builder.build(); return builder.build();
} }
@ -213,14 +205,8 @@ public class CalculatedFieldUtils {
.stream() .stream()
.map(GeofencingZoneState::new) .map(GeofencingZoneState::new)
.collect(Collectors.toMap(GeofencingZoneState::getZoneId, Function.identity())); .collect(Collectors.toMap(GeofencingZoneState::getZoneId, Function.identity()));
List<GeofencingEvent> geofencingEvents = proto.getReportEventsList()
.stream()
.map(geofencingEventProto -> GeofencingEvent.fromProtoNumber(geofencingEventProto.getNumber()))
.toList();
var zoneGroupConfiguration = new GeofencingZoneGroupConfiguration(proto.getTelemetryPrefix(), geofencingEvents);
GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry(); GeofencingArgumentEntry geofencingArgumentEntry = new GeofencingArgumentEntry();
geofencingArgumentEntry.setZoneStates(zoneStates); geofencingArgumentEntry.setZoneStates(zoneStates);
geofencingArgumentEntry.setZoneGroupConfiguration(zoneGroupConfiguration);
return geofencingArgumentEntry; return geofencingArgumentEntry;
} }

View File

@ -40,6 +40,8 @@ public class GeofencingCalculatedFieldConfiguration extends BaseCalculatedFieldC
ENTITY_ID_LONGITUDE_ARGUMENT_KEY ENTITY_ID_LONGITUDE_ARGUMENT_KEY
); );
private String zoneRelationType;
private boolean trackZoneRelations;
private Map<String, GeofencingZoneGroupConfiguration> geofencingZoneGroupConfigurations; private Map<String, GeofencingZoneGroupConfiguration> geofencingZoneGroupConfigurations;
@Override @Override

View File

@ -15,35 +15,8 @@
*/ */
package org.thingsboard.server.common.data.cf.configuration; package org.thingsboard.server.common.data.cf.configuration;
import lombok.Getter;
import java.util.Arrays;
@Getter
public enum GeofencingEvent { public enum GeofencingEvent {
ENTERED(0), LEFT(1), INSIDE(2), OUTSIDE(3); ENTERED, LEFT, INSIDE, OUTSIDE;
private final int protoNumber; // Corresponds to GeofencingEvent
GeofencingEvent(int protoNumber) {
this.protoNumber = protoNumber;
}
private static final GeofencingEvent[] BY_PROTO;
static {
BY_PROTO = new GeofencingEvent[Arrays.stream(values()).mapToInt(GeofencingEvent::getProtoNumber).max().orElse(0) + 1];
for (var event : values()) {
BY_PROTO[event.getProtoNumber()] = event;
}
}
public static GeofencingEvent fromProtoNumber(int protoNumber) {
if (protoNumber < 0 || protoNumber >= BY_PROTO.length) {
throw new IllegalArgumentException("Invalid GeofencingEvent proto number " + protoNumber);
}
return BY_PROTO[protoNumber];
}
} }

View File

@ -908,17 +908,8 @@ message GeofencingZoneProto {
optional bool inside = 5; optional bool inside = 5;
} }
enum GeofencingEventProto {
ENTERED = 0;
LEFT = 1;
INSIDE = 2;
OUTSIDE = 3;
}
message GeofencingArgumentProto { message GeofencingArgumentProto {
string argName = 1; string argName = 1;
string telemetryPrefix = 2;
repeated GeofencingEventProto reportEvents = 3;
repeated GeofencingZoneProto zones = 4; repeated GeofencingZoneProto zones = 4;
} }