From cb343f4f4efebb58c45477d7b90d3d6992c7f4be Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Fri, 17 Feb 2023 09:24:23 +0100 Subject: [PATCH] [New][Backport] New methods on StrolchTimedState and ITimeVariable --- .../timedstate/AbstractStrolchTimedState.java | 20 +++++ .../model/timedstate/StrolchTimedState.java | 78 +++++++++++++++++++ .../model/timevalue/ITimeVariable.java | 17 +++- .../model/timevalue/impl/TimeVariable.java | 42 +++++++++- .../timedstate/StrolchTimedStateTest.java | 73 ++++++++++++++++- 5 files changed, 223 insertions(+), 7 deletions(-) diff --git a/li.strolch.model/src/main/java/li/strolch/model/timedstate/AbstractStrolchTimedState.java b/li.strolch.model/src/main/java/li/strolch/model/timedstate/AbstractStrolchTimedState.java index 7a60ccd78..e4bfc12fc 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/timedstate/AbstractStrolchTimedState.java +++ b/li.strolch.model/src/main/java/li/strolch/model/timedstate/AbstractStrolchTimedState.java @@ -89,6 +89,16 @@ public abstract class AbstractStrolchTimedState extends Abstra } } + @Override + public boolean isInterpretationDefined() { + return !INTERPRETATION_NONE.equals(this.interpretation); + } + + @Override + public boolean isInterpretationEmpty() { + return INTERPRETATION_NONE.equals(this.interpretation); + } + @Override public String getUom() { return this.uom; @@ -104,6 +114,16 @@ public abstract class AbstractStrolchTimedState extends Abstra } } + @Override + public boolean isUomDefined() { + return !UOM_NONE.equals(this.uom); + } + + @Override + public boolean isUomEmpty() { + return UOM_NONE.equals(this.uom); + } + @Override public void setIndex(int index) { assertNotReadonly(); diff --git a/li.strolch.model/src/main/java/li/strolch/model/timedstate/StrolchTimedState.java b/li.strolch.model/src/main/java/li/strolch/model/timedstate/StrolchTimedState.java index 4d6ad36ce..bb0daa8a1 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/timedstate/StrolchTimedState.java +++ b/li.strolch.model/src/main/java/li/strolch/model/timedstate/StrolchTimedState.java @@ -15,6 +15,10 @@ */ package li.strolch.model.timedstate; +import java.time.ZonedDateTime; +import java.util.Iterator; +import java.util.NavigableSet; + import li.strolch.model.Resource; import li.strolch.model.StrolchElement; import li.strolch.model.StrolchModelConstants; @@ -66,6 +70,20 @@ public interface StrolchTimedState extends StrolchElement { */ void setUom(String uom); + /** + * Returns true if the UOM is not {@link StrolchModelConstants#UOM_NONE} + * + * @return true if the UOM is not {@link StrolchModelConstants#UOM_NONE} + */ + boolean isUomDefined(); + + /** + * Returns true if the UOM is set to {@link StrolchModelConstants#UOM_NONE} + * + * @return true if the UOM is set to {@link StrolchModelConstants#UOM_NONE} + */ + boolean isUomEmpty(); + /** * Returns the index of this {@link Parameter}. This can be used to sort the parameters in a UI * @@ -108,6 +126,20 @@ public interface StrolchTimedState extends StrolchElement { */ void setInterpretation(String interpretation); + /** + * Returns true if the interpretation is not {@link StrolchModelConstants#INTERPRETATION_NONE} + * + * @return true if the interpretation is not {@link StrolchModelConstants#INTERPRETATION_NONE} + */ + boolean isInterpretationDefined(); + + /** + * Returns true if the interpretation is set to {@link StrolchModelConstants#INTERPRETATION_NONE} + * + * @return true if the interpretation is set to {@link StrolchModelConstants#INTERPRETATION_NONE} + */ + boolean isInterpretationEmpty(); + ITimeValue getNextMatch(Long time, T value); ITimeValue getPreviousMatch(Long time, T value); @@ -134,4 +166,50 @@ public interface StrolchTimedState extends StrolchElement { StrolchTimedState getClone(); void clear(); + + /** + * Trims this timed state, so it has at most the given number of values + * + * @param maxValues + * the number of values to keep + * + * @return true if the state was trimmed, false if not + */ + default boolean trim(int maxValues) { + assertNotReadonly(); + + ITimeVariable timeEvolution = getTimeEvolution(); + NavigableSet> values = timeEvolution.getValues(); + if (values.size() < maxValues) + return false; + + Iterator> iterator = values.descendingIterator(); + ITimeValue next = iterator.next(); + for (int i = 0; i < maxValues - 1; i++) { + next = iterator.next(); + } + + return !timeEvolution.removePastValues(next.getTime()).isEmpty(); + } + + /** + * Trims this timed state, so all values before the given time stamp + * + * @param timeStamp + * the max date the values may have + * @param keepLastValue + * if true, and the last value is before the max + * + * @return true if the state was trimmed, false if not + */ + default boolean trim(ZonedDateTime timeStamp, boolean keepLastValue) { + assertNotReadonly(); + ITimeVariable timeEvolution = getTimeEvolution(); + + long time = timeStamp.toInstant().toEpochMilli(); + if (keepLastValue && timeEvolution.getFutureValues(time).isEmpty()) + time = timeEvolution.getValueAt(time).getTime(); + + return !timeEvolution.removePastValues(time).isEmpty(); + } } diff --git a/li.strolch.model/src/main/java/li/strolch/model/timevalue/ITimeVariable.java b/li.strolch.model/src/main/java/li/strolch/model/timevalue/ITimeVariable.java index 2aec59217..a34acadad 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/timevalue/ITimeVariable.java +++ b/li.strolch.model/src/main/java/li/strolch/model/timevalue/ITimeVariable.java @@ -60,7 +60,7 @@ public interface ITimeVariable { * @param time * the time the sequence starts with * - * Note: The returned result is unmodifiable + * Note: The returned result is unmodifiable * * @return the sequence of {@link ITimeValue} objects in the future */ @@ -82,7 +82,7 @@ public interface ITimeVariable { * @param time * the time the sequence starts with * - * Note: The returned result is unmodifiable + * Note: The returned result is unmodifiable * * @return the sequence of {@link ITimeValue} objects in the future */ @@ -144,4 +144,17 @@ public interface ITimeVariable { * parent */ void setReadonly(); + + /** + * Returns the number of values stored on this time variable + * + * @return the number of values stored on this time variable + */ + int size(); + + /** + * Returns true if the given {@link ITimeVariable} is equal to this {@link ITimeVariable} by validating that the + * values {@link IValue IValues} have the same time and actual value + */ + boolean equals(ITimeVariable other); } diff --git a/li.strolch.model/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java b/li.strolch.model/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java index 9b15cdb75..fbf59651a 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java +++ b/li.strolch.model/src/main/java/li/strolch/model/timevalue/impl/TimeVariable.java @@ -18,10 +18,7 @@ package li.strolch.model.timevalue.impl; import static java.util.Collections.unmodifiableNavigableSet; import java.io.Serializable; -import java.util.Iterator; -import java.util.NavigableSet; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.*; import java.util.stream.Stream; import li.strolch.exception.StrolchModelException; @@ -39,6 +36,11 @@ public class TimeVariable implements ITimeVariable, Seriali public NavigableSet> container = new TreeSet<>(); private boolean readonly; + @Override + public int size() { + return this.container.size(); + } + @Override public ITimeValue getValueAt(long time) { return this.container.floor(new TimeValue<>(time, null)); @@ -170,4 +172,36 @@ public class TimeVariable implements ITimeVariable, Seriali + " is currently readOnly, to modify clone first!"); } } + + @Override + public boolean equals(ITimeVariable o) { + return equals((Object) o); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + @SuppressWarnings("unchecked") + TimeVariable other = (TimeVariable) o; + if (this.container.size() != other.container.size()) + return false; + Iterator> thisIter = this.container.iterator(); + Iterator> thatIter = other.container.iterator(); + while (thisIter.hasNext()) { + ITimeValue thisNext = thisIter.next(); + ITimeValue thatNext = thatIter.next(); + if (!thisNext.equals(thatNext)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + return Objects.hash(this.container); + } } diff --git a/li.strolch.model/src/test/java/li/strolch/model/timedstate/StrolchTimedStateTest.java b/li.strolch.model/src/test/java/li/strolch/model/timedstate/StrolchTimedStateTest.java index 1db7023ab..07e801ce6 100644 --- a/li.strolch.model/src/test/java/li/strolch/model/timedstate/StrolchTimedStateTest.java +++ b/li.strolch.model/src/test/java/li/strolch/model/timedstate/StrolchTimedStateTest.java @@ -18,8 +18,10 @@ package li.strolch.model.timedstate; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static li.strolch.model.ModelGenerator.*; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; import java.util.HashSet; import java.util.Set; @@ -129,6 +131,75 @@ public class StrolchTimedStateTest { assertEquals(asSet(STATE_STRING_TIME_30), valueAt30.getValue().getValue()); } + @Test + public void testTrimTimedState1() { + + Resource myRes = createResource("@1", "Test With States", "Stated"); + FloatTimedState floatState = myRes.getTimedState(STATE_FLOAT_ID); + + boolean trimmed = floatState.trim(20); + assertFalse(trimmed); + assertEquals(4, floatState.getTimeEvolution().getValues().size()); + + long now = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(20).toInstant().toEpochMilli(); + + for (int i = 0; i < 16; i++) { + floatState.getTimeEvolution().setValueAt(now, new FloatValue(i)); + now += 10; + } + + assertEquals(20, floatState.getTimeEvolution().getValues().size()); + trimmed = floatState.trim(20); + assertFalse(trimmed); + } + + @Test + public void testTrimTimedState2() { + + Resource myRes = createResource("@1", "Test With States", "Stated"); + FloatTimedState floatState = myRes.getTimedState(STATE_FLOAT_ID); + + assertFalse(floatState.trim(20)); + assertEquals(4, floatState.getTimeEvolution().getValues().size()); + + long now = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(20).toInstant().toEpochMilli(); + + for (int i = 0; i < 50; i++) { + floatState.getTimeEvolution().setValueAt(now, new FloatValue(i)); + now += 10; + } + + assertEquals(54, floatState.getTimeEvolution().getValues().size()); + assertTrue(floatState.trim(20)); + assertEquals(20, floatState.getTimeEvolution().getValues().size()); + } + + @Test + public void testTrimTimedState3() { + ZonedDateTime now = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(20); + + Resource myRes = createResource("@1", "Test With States", "Stated"); + FloatTimedState floatState = myRes.getTimedState(STATE_FLOAT_ID); + assertEquals(4, floatState.getTimeEvolution().getValues().size()); + + assertTrue(floatState.trim(now, true)); + assertEquals(1, floatState.getTimeEvolution().getValues().size()); + assertEquals(STATE_TIME_30, floatState.getTimeEvolution().getValues().iterator().next().getTime().longValue()); + + assertFalse(floatState.trim(now, true)); + assertEquals(1, floatState.getTimeEvolution().getValues().size()); + + assertTrue(floatState.trim(now, false)); + assertEquals(0, floatState.getTimeEvolution().getValues().size()); + + ZonedDateTime later = now.plusDays(1); + floatState.setStateFromStringAt(later.toInstant().toEpochMilli(), "55.0"); + floatState.setStateFromStringAt(now.toInstant().toEpochMilli(), "56.0"); + assertEquals(2, floatState.getTimeEvolution().getValues().size()); + assertFalse(floatState.trim(now, true)); + assertEquals(2, floatState.getTimeEvolution().getValues().size()); + } + private static Set asSet(String value) { HashSet hashSet = new HashSet<>(); hashSet.add(new AString(value));