mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Fix group calculations to use GroupItem's system unit (#4563)
* QuantityType improve group calculations * support inverse units, extend tests * refactoring * javadoc and code simplification * use streams; make sums absolute Signed-off-by: Andrew Fiddian-Green <software@whitebear.ch>
This commit is contained in:
+21
-27
@@ -15,7 +15,7 @@ package org.openhab.core.internal.items;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.measure.Quantity;
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -48,13 +48,12 @@ public class GroupFunctionHelper {
|
||||
* arithmetic group function will take unit conversion into account.
|
||||
*
|
||||
* @param function the {@link GroupFunctionDTO} describing the group function.
|
||||
* @param baseItem an optional {@link Item} defining the dimension for unit conversion.
|
||||
* @param baseItem an optional {@link Item} defining the dimension/unit for unit conversion.
|
||||
* @return a {@link GroupFunction} according to the given parameters.
|
||||
*/
|
||||
public GroupFunction createGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem) {
|
||||
Class<? extends Quantity<?>> dimension = getDimension(baseItem);
|
||||
if (dimension != null) {
|
||||
return createDimensionGroupFunction(function, baseItem, dimension);
|
||||
if (baseItem instanceof NumberItem baseNumberItem && baseNumberItem.getDimension() != null) {
|
||||
return createDimensionGroupFunction(function, baseNumberItem);
|
||||
}
|
||||
return createDefaultGroupFunction(function, baseItem);
|
||||
}
|
||||
@@ -78,30 +77,25 @@ public class GroupFunctionHelper {
|
||||
return states;
|
||||
}
|
||||
|
||||
private @Nullable Class<? extends Quantity<?>> getDimension(@Nullable Item baseItem) {
|
||||
if (baseItem instanceof NumberItem numberItem) {
|
||||
return numberItem.getDimension();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem,
|
||||
Class<? extends Quantity<?>> dimension) {
|
||||
private GroupFunction createDimensionGroupFunction(GroupFunctionDTO function, NumberItem baseItem) {
|
||||
final String functionName = function.name;
|
||||
switch (functionName.toUpperCase()) {
|
||||
case "AVG":
|
||||
return new QuantityTypeArithmeticGroupFunction.Avg(dimension);
|
||||
case "MEDIAN":
|
||||
return new QuantityTypeArithmeticGroupFunction.Median(dimension, baseItem);
|
||||
case "SUM":
|
||||
return new QuantityTypeArithmeticGroupFunction.Sum(dimension);
|
||||
case "MIN":
|
||||
return new QuantityTypeArithmeticGroupFunction.Min(dimension);
|
||||
case "MAX":
|
||||
return new QuantityTypeArithmeticGroupFunction.Max(dimension);
|
||||
default:
|
||||
return createDefaultGroupFunction(function, baseItem);
|
||||
Unit<?> baseItemUnit = baseItem.getUnit();
|
||||
if (baseItemUnit != null) {
|
||||
switch (functionName.toUpperCase()) {
|
||||
case "AVG":
|
||||
return new QuantityTypeArithmeticGroupFunction.Avg(baseItemUnit);
|
||||
case "MEDIAN":
|
||||
return new QuantityTypeArithmeticGroupFunction.Median(baseItemUnit);
|
||||
case "SUM":
|
||||
return new QuantityTypeArithmeticGroupFunction.Sum(baseItemUnit);
|
||||
case "MIN":
|
||||
return new QuantityTypeArithmeticGroupFunction.Min(baseItemUnit);
|
||||
case "MAX":
|
||||
return new QuantityTypeArithmeticGroupFunction.Max(baseItemUnit);
|
||||
default:
|
||||
}
|
||||
}
|
||||
return createDefaultGroupFunction(function, baseItem);
|
||||
}
|
||||
|
||||
private GroupFunction createDefaultGroupFunction(GroupFunctionDTO function, @Nullable Item baseItem) {
|
||||
|
||||
+127
-153
@@ -13,12 +13,11 @@
|
||||
package org.openhab.core.library.types;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.measure.Quantity;
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@@ -26,31 +25,38 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.items.GroupFunction;
|
||||
import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.openhab.core.util.Statistics;
|
||||
|
||||
import tech.units.indriya.AbstractUnit;
|
||||
|
||||
/**
|
||||
* This interface is a container for dimension based functions that require {@link QuantityType}s for its calculations.
|
||||
*
|
||||
* @author Henning Treu - Initial contribution
|
||||
* @author Andrew Fiddian-Green - Normalise calculations based on the Unit of the GroupItem
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface QuantityTypeArithmeticGroupFunction extends GroupFunction {
|
||||
|
||||
abstract class DimensionalGroupFunction implements GroupFunction {
|
||||
|
||||
protected final Class<? extends Quantity<?>> dimension;
|
||||
protected final Unit<?> baseItemUnit; // the actual unit of the owning group item
|
||||
protected final Unit<?> systemUnit; // the reference unit for group member calculations
|
||||
|
||||
public DimensionalGroupFunction(Class<? extends Quantity<?>> dimension) {
|
||||
this.dimension = dimension;
|
||||
public DimensionalGroupFunction(Unit<?> baseItemUnit) {
|
||||
this.baseItemUnit = baseItemUnit;
|
||||
this.systemUnit = baseItemUnit.getSystemUnit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T extends State> T getStateAs(@Nullable Set<Item> items, Class<T> stateClass) {
|
||||
State state = calculate(items);
|
||||
if (stateClass.isInstance(state)) {
|
||||
if (state instanceof QuantityType<?> quantity) {
|
||||
state = toInvertibleUnit(quantity, baseItemUnit);
|
||||
}
|
||||
return stateClass.cast(state);
|
||||
} else {
|
||||
return null;
|
||||
@@ -62,102 +68,76 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction {
|
||||
return new State[0];
|
||||
}
|
||||
|
||||
protected boolean isSameDimension(@Nullable Item item) {
|
||||
if (item instanceof GroupItem groupItem) {
|
||||
return isSameDimension(groupItem.getBaseItem());
|
||||
/**
|
||||
* Convert the given {@link QuantityType} to an equivalent based on the given {@link Unit}. The conversion can
|
||||
* be made to both inverted and non-inverted units, so invertible type conversions (e.g. Mirek <=> Kelvin) are
|
||||
* supported.
|
||||
* <p>
|
||||
* Note: we can use {@link QuantityType.toInvertibleUnit()} if OH Core PR #4561 is merged.
|
||||
*
|
||||
* @param source the {@link QuantityType} to be converted.
|
||||
* @param targetUnit the {@link Unit} to convert to.
|
||||
*
|
||||
* @return a new {@link QuantityType} based on 'targetUnit' or null.
|
||||
*/
|
||||
private @Nullable QuantityType<?> toInvertibleUnit(QuantityType<?> source, Unit<?> targetUnit) {
|
||||
if (!targetUnit.equals(source.getUnit()) && !targetUnit.isCompatible(AbstractUnit.ONE)
|
||||
&& source.getUnit().inverse().isCompatible(targetUnit)) {
|
||||
QuantityType<?> sourceInSystemUnit = source.toUnit(source.getUnit().getSystemUnit());
|
||||
return sourceInSystemUnit != null ? sourceInSystemUnit.inverse().toUnit(targetUnit) : null;
|
||||
}
|
||||
return item instanceof NumberItem ni && dimension.equals(ni.getDimension());
|
||||
return source.toUnit(targetUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given item {@link State} to a {@link QuantityType} based on the {@link Unit} of the
|
||||
* {@link GroupItem} i.e. 'referenceUnit'. Returns null if the {@link State} is not a {@link QuantityType} or
|
||||
* if the {@link QuantityType} could not be converted to 'referenceUnit'.
|
||||
*
|
||||
* The conversion can be made to both inverted and non-inverted units, so invertible type conversions (e.g.
|
||||
* Mirek <=> Kelvin) are supported.
|
||||
*
|
||||
* @param state the State of any given group member item
|
||||
* @return a QuantityType or null
|
||||
*/
|
||||
private @Nullable QuantityType<?> toQuantityTypeOfUnit(@Nullable State state, Unit<?> unit) {
|
||||
return state instanceof QuantityType<?> quantity //
|
||||
? toInvertibleUnit(quantity, unit)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a set of {@link Item} to a respective list of {@link QuantityType}. Exclude any {@link Item}s whose
|
||||
* current {@link State} is not a {@link QuantityType}. Convert any remaining {@link QuantityType} to the
|
||||
* 'referenceUnit' and exclude any values that did not convert.
|
||||
*
|
||||
* @param items a list of {@link Item}
|
||||
* @return a list of {@link QuantityType} converted to the 'referenceUnit'
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
protected List<QuantityType> toQuantityTypesOfUnit(Set<Item> items, Unit<?> unit) {
|
||||
return items.stream().map(i -> i.getState()).map(s -> toQuantityTypeOfUnit(s, unit))
|
||||
.filter(Objects::nonNull).map(s -> (QuantityType) s).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This calculates the numeric average over all item states of {@link QuantityType}.
|
||||
* Calculates the average of a set of item states whose value could be converted to the 'referenceUnit'.
|
||||
*/
|
||||
class Avg extends DimensionalGroupFunction {
|
||||
|
||||
public Avg(Class<? extends Quantity<?>> dimension) {
|
||||
super(dimension);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
QuantityType<?> sum = null;
|
||||
int count = 0;
|
||||
for (Item item : items) {
|
||||
if (isSameDimension(item)) {
|
||||
QuantityType itemState = item.getStateAs(QuantityType.class);
|
||||
if (itemState != null) {
|
||||
if (sum == null) {
|
||||
sum = itemState; // initialise the sum from the first item
|
||||
count++;
|
||||
} else {
|
||||
itemState = itemState.toInvertibleUnit(sum.getUnit());
|
||||
if (itemState != null) {
|
||||
sum = sum.add(itemState);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sum != null && count > 0) {
|
||||
BigDecimal result = sum.toBigDecimal().divide(BigDecimal.valueOf(count), MathContext.DECIMAL128);
|
||||
return new QuantityType(result, sum.getUnit());
|
||||
}
|
||||
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This calculates the numeric median over all item states of {@link QuantityType}.
|
||||
*/
|
||||
class Median extends DimensionalGroupFunction {
|
||||
|
||||
private @Nullable Item baseItem;
|
||||
|
||||
public Median(Class<? extends Quantity<?>> dimension, @Nullable Item baseItem) {
|
||||
super(dimension);
|
||||
this.baseItem = baseItem;
|
||||
public Avg(Unit<?> baseItemUnit) {
|
||||
super(baseItemUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items != null) {
|
||||
List<BigDecimal> values = new ArrayList<>();
|
||||
Unit<?> unit = null;
|
||||
if (baseItem instanceof NumberItem numberItem) {
|
||||
unit = numberItem.getUnit();
|
||||
}
|
||||
for (Item item : items) {
|
||||
if (!isSameDimension(item)) {
|
||||
continue;
|
||||
}
|
||||
QuantityType itemState = item.getStateAs(QuantityType.class);
|
||||
if (itemState == null) {
|
||||
continue;
|
||||
}
|
||||
if (unit == null) {
|
||||
unit = itemState.getUnit(); // set it to the first item's unit
|
||||
}
|
||||
if (itemState.toInvertibleUnit(unit) instanceof QuantityType<?> inverted) {
|
||||
values.add(inverted.toBigDecimal());
|
||||
}
|
||||
}
|
||||
|
||||
if (!values.isEmpty()) {
|
||||
BigDecimal median = Statistics.median(values);
|
||||
if (median != null && unit != null) {
|
||||
return new QuantityType<>(median, unit);
|
||||
}
|
||||
|
||||
List<QuantityType> systemUnitQuantities = toQuantityTypesOfUnit(items, systemUnit);
|
||||
if (!systemUnitQuantities.isEmpty()) {
|
||||
return systemUnitQuantities.stream().reduce(new QuantityType<>(0, systemUnit), QuantityType::add)
|
||||
.divide(BigDecimal.valueOf(systemUnitQuantities.size()));
|
||||
}
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
@@ -165,105 +145,99 @@ public interface QuantityTypeArithmeticGroupFunction extends GroupFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* This calculates the numeric sum over all item states of {@link QuantityType}.
|
||||
* Calculates the median of a set of item states whose value could be converted to the 'referenceUnit'.
|
||||
*/
|
||||
class Median extends DimensionalGroupFunction {
|
||||
|
||||
public Median(Unit<?> baseItemUnit) {
|
||||
super(baseItemUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items != null) {
|
||||
BigDecimal median = Statistics
|
||||
.median(toQuantityTypesOfUnit(items, systemUnit).stream().map(q -> q.toBigDecimal()).toList());
|
||||
if (median != null) {
|
||||
return new QuantityType<>(median, systemUnit);
|
||||
}
|
||||
}
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the sum of a set of item states whose value could be converted to the 'referenceUnit'.
|
||||
*
|
||||
* Uses the {@link QuanitityType.add()} method so the result is an incremental sum based on the 'referenceUnit'. As
|
||||
* a general rule this class is instantiated with a 'referenceUnit' that is a "system unit" (which are zero based)
|
||||
* so such incremental sum is in fact also an absolute sum. However the class COULD be instantiated with a "non-
|
||||
* system unit" (e.g. °C, °F) in which case the result would be an incremental sum based on that unit.
|
||||
*
|
||||
*/
|
||||
class Sum extends DimensionalGroupFunction {
|
||||
|
||||
public Sum(Class<? extends Quantity<?>> dimension) {
|
||||
super(dimension);
|
||||
public Sum(Unit<?> baseItemUnit) {
|
||||
super(baseItemUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings("unchecked")
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
QuantityType<?> sum = null;
|
||||
for (Item item : items) {
|
||||
if (isSameDimension(item)) {
|
||||
QuantityType itemState = item.getStateAs(QuantityType.class);
|
||||
if (itemState != null) {
|
||||
if (sum == null) {
|
||||
sum = itemState; // initialise the sum from the first item
|
||||
} else {
|
||||
itemState = itemState.toUnit(sum.getUnit());
|
||||
if (itemState != null) {
|
||||
sum = sum.add(itemState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (items != null) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
List<QuantityType> systemUnitQuantities = toQuantityTypesOfUnit(items, baseItemUnit);
|
||||
if (!systemUnitQuantities.isEmpty()) {
|
||||
return systemUnitQuantities.stream().reduce(new QuantityType<>(0, baseItemUnit), QuantityType::add);
|
||||
}
|
||||
}
|
||||
|
||||
return sum != null ? sum : UnDefType.UNDEF;
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This calculates the minimum value of all item states of {@link QuantityType}.
|
||||
* Calculates the minimum of a set of item states whose value could be converted to the 'referenceUnit'.
|
||||
*/
|
||||
class Min extends DimensionalGroupFunction {
|
||||
|
||||
public Min(Class<? extends Quantity<?>> dimension) {
|
||||
super(dimension);
|
||||
public Min(Unit<?> baseItemUnit) {
|
||||
super(baseItemUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
QuantityType<?> min = null;
|
||||
for (Item item : items) {
|
||||
if (isSameDimension(item)) {
|
||||
QuantityType itemState = item.getStateAs(QuantityType.class);
|
||||
if (itemState != null) {
|
||||
if (min == null
|
||||
|| (min.getUnit().isCompatible(itemState.getUnit()) && min.compareTo(itemState) > 0)) {
|
||||
min = itemState;
|
||||
}
|
||||
}
|
||||
if (items != null) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
Optional<QuantityType> min = toQuantityTypesOfUnit(items, systemUnit).stream()
|
||||
.min(QuantityType::compareTo);
|
||||
if (min.isPresent()) {
|
||||
return min.get();
|
||||
}
|
||||
}
|
||||
|
||||
return min != null ? min : UnDefType.UNDEF;
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This calculates the maximum value of all item states of {@link QuantityType}.
|
||||
* Calculates the maximum of a set of item states whose value could be converted to the 'referenceUnit'.
|
||||
*/
|
||||
class Max extends DimensionalGroupFunction {
|
||||
|
||||
public Max(Class<? extends Quantity<?>> dimension) {
|
||||
super(dimension);
|
||||
public Max(Unit<?> targetUnit) {
|
||||
super(targetUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public State calculate(@Nullable Set<Item> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
|
||||
QuantityType<?> max = null;
|
||||
for (Item item : items) {
|
||||
if (isSameDimension(item)) {
|
||||
QuantityType itemState = item.getStateAs(QuantityType.class);
|
||||
if (itemState != null) {
|
||||
if (max == null
|
||||
|| (max.getUnit().isCompatible(itemState.getUnit()) && max.compareTo(itemState) < 0)) {
|
||||
max = itemState;
|
||||
}
|
||||
}
|
||||
if (items != null) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
Optional<QuantityType> max = toQuantityTypesOfUnit(items, systemUnit).stream()
|
||||
.max(QuantityType::compareTo);
|
||||
if (max.isPresent()) {
|
||||
return max.get();
|
||||
}
|
||||
}
|
||||
|
||||
return max != null ? max : UnDefType.UNDEF;
|
||||
return UnDefType.UNDEF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+209
-17
@@ -15,7 +15,7 @@ package org.openhab.core.library.types;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.number.IsCloseTo.closeTo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -46,6 +46,9 @@ import org.openhab.core.items.GroupItem;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.library.CoreItemFactory;
|
||||
import org.openhab.core.library.items.NumberItem;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.osgi.service.component.ComponentContext;
|
||||
@@ -85,7 +88,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("234.95 °C"), state);
|
||||
@@ -103,7 +106,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("234.95 °C"), state);
|
||||
@@ -119,7 +122,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("23.54 °C"), state);
|
||||
@@ -137,7 +140,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("200 °C"), state);
|
||||
@@ -163,10 +166,11 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state);
|
||||
assertTrue(state instanceof QuantityType<?>);
|
||||
assertEquals(328.48, ((QuantityType<?>) state).doubleValue(), 0.01);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@@ -179,12 +183,13 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("23.54 °C"), state);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static Stream<Arguments> medianTestSource() {
|
||||
return Stream.of( //
|
||||
arguments( //
|
||||
@@ -214,13 +219,14 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("medianTestSource")
|
||||
@SuppressWarnings({ "null", "rawtypes", "unchecked" })
|
||||
public void testMedianFunctionQuantityType(List<State> states, State expected) {
|
||||
AtomicInteger index = new AtomicInteger(1);
|
||||
Set<Item> items = states.stream()
|
||||
.map(state -> createNumberItem("TestItem" + index.getAndIncrement(), Temperature.class, state))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(Temperature.class, null);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Median(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(state.getClass(), expected.getClass());
|
||||
@@ -242,7 +248,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("300 °C"), state);
|
||||
@@ -260,7 +266,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("100 °C"), state);
|
||||
@@ -276,7 +282,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("23.54 °C"), state);
|
||||
@@ -294,7 +300,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("100 °C"), state);
|
||||
@@ -313,7 +319,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem5", Dimensionless.class, new QuantityType<>("0 %")));
|
||||
items.add(createNumberItem("TestItem6", Dimensionless.class, new QuantityType<>("0 %")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.ONE);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("48 %"), state);
|
||||
@@ -331,7 +337,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("294.15 K"), state);
|
||||
@@ -347,7 +353,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(SIUnits.CELSIUS);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("23.54 °C"), state);
|
||||
@@ -362,7 +368,7 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W")));
|
||||
items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class);
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.WATT);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("10 W"), state);
|
||||
@@ -380,4 +386,190 @@ public class QuantityTypeArithmeticGroupFunctionTest {
|
||||
item.setState(state);
|
||||
return item;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testSumFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("192.2 °F")));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("1054.40 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testAvgFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertTrue(state instanceof QuantityType<?>);
|
||||
assertEquals(328.48, ((QuantityType<?>) state).doubleValue(), 0.01);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testMaxFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("100 °C"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testMinFunctionQuantityTypeDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("294.15 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testSumFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN)));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED)));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class,
|
||||
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT)));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("8000 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN)));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED)));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class,
|
||||
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT)));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("2000 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testAvgFunctionQuantityTypeColorTempDifferentUnitsBaseMirek(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2000, Units.KELVIN)));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED)));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class,
|
||||
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT)));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Units.MIRED);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("2000 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testMinFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(1999, Units.KELVIN)));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED)));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class,
|
||||
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT)));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("1999 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testMaxFunctionColorTemperatureDifferentUnitsBaseKelvin(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Temperature.class, QuantityType.valueOf(2001, Units.KELVIN)));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, QuantityType.valueOf(1726.85, SIUnits.CELSIUS)));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, QuantityType.valueOf(500, Units.MIRED)));
|
||||
items.add(createNumberItem("TestItem5", Temperature.class,
|
||||
QuantityType.valueOf(3140.33, ImperialUnits.FAHRENHEIT)));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Units.KELVIN);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("2001 K"), state);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("locales")
|
||||
public void testSumFunctionQuantityTypeDifferentUnitsBaseWatt(Locale locale) {
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Set<Item> items = new LinkedHashSet<>();
|
||||
items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("1 W")));
|
||||
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
|
||||
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("192.2 °F")));
|
||||
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
|
||||
items.add(createNumberItem("TestItem5", Power.class, new QuantityType<>("3000 mW")));
|
||||
|
||||
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Units.WATT);
|
||||
State state = function.calculate(items);
|
||||
|
||||
assertEquals(new QuantityType<>("4 W"), state);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user