24 changed files with 672 additions and 6 deletions
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.cf.ctx.state.aggregation.single; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class AggIntervalEntry { |
|||
|
|||
public Long startTs; |
|||
public Long endTs; |
|||
|
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.cf.ctx.state.aggregation.single; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import lombok.Setter; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.actors.TbActorRef; |
|||
import org.thingsboard.server.common.data.cf.CalculatedFieldType; |
|||
import org.thingsboard.server.common.data.cf.configuration.Output; |
|||
import org.thingsboard.server.common.data.cf.configuration.aggregation.single.EntityAggregationCalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService; |
|||
import org.thingsboard.server.service.cf.CalculatedFieldResult; |
|||
import org.thingsboard.server.service.cf.TelemetryCalculatedFieldResult; |
|||
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry; |
|||
import org.thingsboard.server.service.cf.ctx.state.BaseCalculatedFieldState; |
|||
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
import static java.util.concurrent.TimeUnit.SECONDS; |
|||
|
|||
public class EntityAggregationCalculatedFieldState extends BaseCalculatedFieldState { |
|||
|
|||
private Map<AggIntervalEntry, Boolean> aggIntervals = new HashMap<>(); |
|||
|
|||
@Setter |
|||
private long lastArgsRefreshTs = -1; |
|||
@Setter |
|||
private long lastMetricsEvalTs = -1; |
|||
|
|||
private long deduplicationIntervalMs = -1; |
|||
|
|||
CalculatedFieldProcessingService cfProcessingService; |
|||
|
|||
public EntityAggregationCalculatedFieldState(EntityId entityId) { |
|||
super(entityId); |
|||
} |
|||
|
|||
@Override |
|||
public void setCtx(CalculatedFieldCtx ctx, TbActorRef actorCtx) { |
|||
super.setCtx(ctx, actorCtx); |
|||
this.cfProcessingService = ctx.getCfProcessingService(); |
|||
var configuration = (EntityAggregationCalculatedFieldConfiguration) ctx.getCalculatedField().getConfiguration(); |
|||
deduplicationIntervalMs = SECONDS.toMillis(configuration.getDeduplicationIntervalInSec()); |
|||
} |
|||
|
|||
@Override |
|||
public CalculatedFieldType getType() { |
|||
return CalculatedFieldType.ENTITY_AGGREGATION; |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<CalculatedFieldResult> performCalculation(Map<String, ArgumentEntry> updatedArgs, CalculatedFieldCtx ctx) throws Exception { |
|||
long endTs = System.currentTimeMillis(); |
|||
long startTs = endTs - 1000; |
|||
AggIntervalEntry interval = new AggIntervalEntry(); |
|||
|
|||
Map<String, ArgumentEntry> metrics = cfProcessingService.fetchArgumentValuesDuringInterval(entityId, interval, ctx); |
|||
|
|||
Output output = ctx.getOutput(); |
|||
lastMetricsEvalTs = System.currentTimeMillis(); |
|||
ctx.scheduleReevaluation(deduplicationIntervalMs, actorCtx); |
|||
ObjectNode result = toResult(endTs, metrics); |
|||
if (result != null) { |
|||
return Futures.immediateFuture(TelemetryCalculatedFieldResult.builder() |
|||
.type(output.getType()) |
|||
.scope(output.getScope()) |
|||
.result(result) |
|||
.build()); |
|||
} |
|||
return Futures.immediateFuture(TelemetryCalculatedFieldResult.EMPTY); |
|||
} |
|||
|
|||
|
|||
protected ObjectNode toResult(long endTs, Map<String, ArgumentEntry> metrics) { |
|||
ObjectNode metricsNode = JacksonUtil.newObjectNode(); |
|||
for (Map.Entry<String, ArgumentEntry> entry : metrics.entrySet()) { |
|||
String metricName = entry.getKey(); |
|||
ArgumentEntry argumentEntry = entry.getValue(); |
|||
if (!argumentEntry.isEmpty()) { |
|||
metricsNode.put(metricName, JacksonUtil.toString(argumentEntry.getValue())); |
|||
} |
|||
} |
|||
ObjectNode resultNode = JacksonUtil.newObjectNode(); |
|||
if (!metricsNode.isEmpty()) { |
|||
resultNode.put("ts", endTs); |
|||
resultNode.set("values", metricsNode); |
|||
} |
|||
return resultNode; |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public boolean isReady() { |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single; |
|||
|
|||
import jakarta.validation.Valid; |
|||
import jakarta.validation.constraints.NotEmpty; |
|||
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.ArgumentsBasedCalculatedFieldConfiguration; |
|||
import org.thingsboard.server.common.data.cf.configuration.Output; |
|||
import org.thingsboard.server.common.data.cf.configuration.aggregation.AggMetric; |
|||
import org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval.AggInterval; |
|||
|
|||
import java.util.Map; |
|||
|
|||
@Data |
|||
public class EntityAggregationCalculatedFieldConfiguration implements ArgumentsBasedCalculatedFieldConfiguration { |
|||
|
|||
private Map<String, Argument> arguments; |
|||
@Valid |
|||
@NotEmpty |
|||
private Map<String, AggMetric> metrics; |
|||
|
|||
private AggInterval interval; |
|||
private long deduplicationIntervalInSec; |
|||
private long watermark; |
|||
|
|||
private Output output; |
|||
|
|||
@Override |
|||
public CalculatedFieldType getType() { |
|||
return CalculatedFieldType.ENTITY_AGGREGATION; |
|||
} |
|||
|
|||
@Override |
|||
public void validate() { |
|||
if (arguments.containsKey("ctx")) { |
|||
throw new IllegalArgumentException("Argument name 'ctx' is reserved and cannot be used."); |
|||
} |
|||
if (arguments.values().stream().anyMatch(Argument::hasTsRollingArgument)) { |
|||
throw new IllegalArgumentException("Calculated field with type: '" + getType() + "' doesn't support TS_ROLLING arguments."); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
|
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
include = JsonTypeInfo.As.PROPERTY, |
|||
property = "type" |
|||
) |
|||
@JsonSubTypes({ |
|||
@JsonSubTypes.Type(value = HourInterval.class, name = "HOUR"), |
|||
@JsonSubTypes.Type(value = DayInterval.class, name = "DAY"), |
|||
@JsonSubTypes.Type(value = WeekInterval.class, name = "WEEK"), |
|||
@JsonSubTypes.Type(value = WeekSunSatInterval.class, name = "WEEK_SUN_SAT"), |
|||
@JsonSubTypes.Type(value = MonthInterval.class, name = "MONTH"), |
|||
@JsonSubTypes.Type(value = YearInterval.class, name = "YEAR"), |
|||
@JsonSubTypes.Type(value = CustomInterval.class, name = "CUSTOM"), |
|||
@JsonSubTypes.Type(value = SpecificTimeInterval.class, name = "SPECIFIC_TIME"), |
|||
}) |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public interface AggInterval { |
|||
|
|||
AggIntervalType getType(); |
|||
|
|||
long getDelayUntilIntervalEnd(); |
|||
|
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public enum AggIntervalType { |
|||
|
|||
HOUR, |
|||
DAY, |
|||
WEEK, |
|||
WEEK_SUN_SAT, |
|||
MONTH, |
|||
YEAR, |
|||
CUSTOM, |
|||
SPECIFIC_TIME |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
import java.time.DayOfWeek; |
|||
import java.time.Duration; |
|||
import java.time.ZonedDateTime; |
|||
import java.time.temporal.ChronoUnit; |
|||
|
|||
public abstract class BaseAggInterval implements AggInterval { |
|||
|
|||
protected long offsetMillis; // delay millis since start of interval
|
|||
|
|||
@Override |
|||
public long getDelayUntilIntervalEnd() { |
|||
return getDelayUntilIntervalEnd(getType(), 1); |
|||
} |
|||
|
|||
protected long getDelayUntilIntervalEnd(AggIntervalType type, long multiplier) { |
|||
ZonedDateTime now = ZonedDateTime.now(); |
|||
ZonedDateTime next; |
|||
|
|||
switch (getType()) { |
|||
case HOUR -> next = now.plusHours(multiplier).truncatedTo(ChronoUnit.HOURS); |
|||
case DAY -> next = now.plusDays(multiplier).truncatedTo(ChronoUnit.DAYS); |
|||
case WEEK -> next = now.plusWeeks(multiplier).with(DayOfWeek.MONDAY).truncatedTo(ChronoUnit.DAYS); |
|||
case WEEK_SUN_SAT -> next = now.plusWeeks(multiplier).with(DayOfWeek.SUNDAY).truncatedTo(ChronoUnit.DAYS); |
|||
case MONTH -> next = now.plusMonths(multiplier).withDayOfMonth(1).truncatedTo(ChronoUnit.DAYS); |
|||
case YEAR -> next = now.plusYears(multiplier).withDayOfYear(1).truncatedTo(ChronoUnit.DAYS); |
|||
default -> throw new IllegalArgumentException("Unsupported type: " + getType()); |
|||
} |
|||
|
|||
long delayMillis = Duration.between(now, next).toMillis(); |
|||
if (offsetMillis > 0) { |
|||
delayMillis += offsetMillis; |
|||
} |
|||
|
|||
return Math.max(delayMillis, 0); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class CustomInterval extends BaseAggInterval { |
|||
|
|||
private int multiplier; // number of base units (e.g. 2 hours, 5 days)
|
|||
private AggIntervalType internalIntervalType; |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.CUSTOM; |
|||
} |
|||
|
|||
@Override |
|||
public long getDelayUntilIntervalEnd() { |
|||
return super.getDelayUntilIntervalEnd(internalIntervalType, multiplier); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class DayInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.DAY; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class HourInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.HOUR; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class MonthInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.MONTH; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
import java.time.LocalTime; |
|||
|
|||
public class SpecificTimeInterval implements AggInterval { |
|||
|
|||
public Long startMillis; // start millis since start of day
|
|||
public Long endMillis; // end millis since start of day
|
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.SPECIFIC_TIME; |
|||
} |
|||
|
|||
@Override |
|||
public long getDelayUntilIntervalEnd() { |
|||
long nowMillis = LocalTime.now().toNanoOfDay() / 1_000_000L; |
|||
long delayMillis; |
|||
if (nowMillis < endMillis) { |
|||
delayMillis = endMillis - nowMillis; // later today
|
|||
} else { |
|||
delayMillis = (24 * 60 * 60 * 1000L - nowMillis) + endMillis; // next day
|
|||
} |
|||
return delayMillis; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class WeekInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.WEEK; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class WeekSunSatInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.WEEK_SUN_SAT; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.cf.configuration.aggregation.single.interval; |
|||
|
|||
public class YearInterval extends BaseAggInterval { |
|||
|
|||
@Override |
|||
public AggIntervalType getType() { |
|||
return AggIntervalType.YEAR; |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue