From 908f3683f6cd21dcff70a8f3b1a9d1e3368ca5af Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Sun, 3 Dec 2017 22:07:15 +0200 Subject: [PATCH] Tested and fixed utility for truncating history data. HistoryUtil.truncateHistory now actually works as intended. Care must be taken not to interfere with the history data while it is being truncated. This must be ensured externally. A reference counter (AtomicInteger) was also added to ChartData to be able to see how many users the ChartData has and to wait until it no longer has any users. This is required to wait until all chart editors have detached themselves from the history data. refs #7622 Change-Id: I35863f5258da2cae193b3086500b5514c4699518 --- .../simantics/charts/editor/ChartData.java | 45 +++++++++++++++++++ .../charts/editor/TimeSeriesEditor.java | 14 +++--- .../history/impl/CollectorState.java | 2 +- .../simulation/history/HistoryUtil.java | 32 +++++++------ 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/ChartData.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/ChartData.java index b44556b63..e85c08037 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/ChartData.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/editor/ChartData.java @@ -11,6 +11,8 @@ *******************************************************************************/ package org.simantics.charts.editor; +import java.util.concurrent.atomic.AtomicInteger; + import org.simantics.databoard.annotations.Optional; import org.simantics.db.Resource; import org.simantics.history.Collector; @@ -58,6 +60,13 @@ public final class ChartData { @Optional public Collector collector; + /** + * This (shared instance) is used to track the amount of users of this ChartData + * instance. The same instance may end up in many ChartData instances through + * {@link #readFrom(ChartData)}. + */ + public AtomicInteger refCount = new AtomicInteger(); + public ChartData(Resource model, Resource run, IExperiment experiment, Datasource datasource, HistoryManager history, Collector collector) { this.model = model; this.run = run; @@ -75,6 +84,7 @@ public final class ChartData { this.datasource = null; this.history = null; this.collector = null; + this.refCount = null; } else { this.model = other.model; this.run = other.run; @@ -82,6 +92,7 @@ public final class ChartData { this.datasource = other.datasource; this.history = other.history; this.collector = other.collector; + this.refCount = other.refCount; } } @@ -99,4 +110,38 @@ public final class ChartData { } } + public int reference() { + AtomicInteger i = refCount; + if (i == null) + return 0; + int result = i.incrementAndGet(); + //System.out.println(this + ": reference: " + (result-1) + " -> " + result + " (" + System.identityHashCode(refCount) + ")"); + return result; + } + + public int dereference() { + AtomicInteger i = refCount; + if (i == null) + return 0; + int result = i.decrementAndGet(); + //System.out.println(this + ": dereference: " + (result+1) + " -> " + result + " (" + System.identityHashCode(refCount) + ")"); + if (result <= 0) { + synchronized (i) { + i.notifyAll(); + } + } + return result; + } + + public void waitUntilNotReferenced() throws InterruptedException { + AtomicInteger i = refCount; + if (i == null) + return; + synchronized (i) { + while (i.get() > 0) { + i.wait(); + } + } + } + } diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java b/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java index 4f633fbe6..cadb5bd4b 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/editor/TimeSeriesEditor.java @@ -586,6 +586,7 @@ public class TimeSeriesEditor extends ResourceEditorPart { // Track data source and preinitialize chartData project.addHintListener(chartDataListener); chartData.readFrom( (ChartData) project.getHint( chartDataKey ) ); + chartData.reference(); if (chartData.run != null) { milestoneListener = new MilestoneSpecListener(); @@ -748,6 +749,7 @@ public class TimeSeriesEditor extends ResourceEditorPart { chartData.datasource.removeListener( stepListener ); if (chartData.experiment!=null) chartData.experiment.removeListener( experimentStateListener ); + chartData.dereference(); chartData.readFrom( null ); } @@ -840,7 +842,9 @@ public class TimeSeriesEditor extends ResourceEditorPart { } if (doLayout) trendNode.layout(); + this.chartData.dereference(); this.chartData.readFrom( data ); + this.chartData.reference(); tp.setDirty(); if (!ObjectUtils.objectEquals(oldExperimentResource, newExperimentResource)) { @@ -989,18 +993,18 @@ public class TimeSeriesEditor extends ResourceEditorPart { tp.setDirty(); } - @SuppressWarnings("rawtypes") + @SuppressWarnings("unchecked") @Override - public Object getAdapter(Class adapter) { + public T getAdapter(Class adapter) { if (adapter == INode.class) { ICanvasContext ctx = cvsCtx; if (ctx != null) - return ctx.getSceneGraph(); + return (T) ctx.getSceneGraph(); } if (adapter == IPropertyPage.class) - return new StandardPropertyPage(getSite(), getPropertyPageContexts()); + return (T) new StandardPropertyPage(getSite(), getPropertyPageContexts()); if (adapter == ICanvasContext.class) - return cvsCtx; + return (T) cvsCtx; return super.getAdapter(adapter); } diff --git a/bundles/org.simantics.history/src/org/simantics/history/impl/CollectorState.java b/bundles/org.simantics.history/src/org/simantics/history/impl/CollectorState.java index bcecf75a5..112cc0206 100644 --- a/bundles/org.simantics.history/src/org/simantics/history/impl/CollectorState.java +++ b/bundles/org.simantics.history/src/org/simantics/history/impl/CollectorState.java @@ -50,7 +50,7 @@ public class CollectorState extends Bean { public static class VariableState { - /** The actual value, type is Void is no value */ + /** The actual value, type is Void if no value */ public MutableVariant value; /** Set to true if is valid value */ diff --git a/bundles/org.simantics.simulation/src/org/simantics/simulation/history/HistoryUtil.java b/bundles/org.simantics.simulation/src/org/simantics/simulation/history/HistoryUtil.java index 53a76d136..27f2bf895 100644 --- a/bundles/org.simantics.simulation/src/org/simantics/simulation/history/HistoryUtil.java +++ b/bundles/org.simantics.simulation/src/org/simantics/simulation/history/HistoryUtil.java @@ -52,14 +52,15 @@ import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceNotFoundException; import org.simantics.db.request.Read; import org.simantics.fastlz.FastLZ; -import org.simantics.history.Collector; import org.simantics.history.HistoryException; import org.simantics.history.HistoryManager; import org.simantics.history.ItemManager; +import org.simantics.history.impl.CollectorImpl; import org.simantics.history.impl.CollectorState; import org.simantics.history.impl.CollectorState.VariableState; import org.simantics.history.util.Stream; import org.simantics.history.util.ValueBand; +import org.simantics.history.util.WeightedMedian; import org.simantics.layer0.Layer0; import org.simantics.simulation.Activator; import org.simantics.simulation.ontology.HistoryResource; @@ -949,17 +950,16 @@ public class HistoryUtil { return 0L; } - public static void truncateHistory(double toBeforeTime, HistoryManager history, Collector collector) throws AccessorException, BindingException, HistoryException { + public static CollectorState truncateHistory(double toBeforeTime, HistoryManager history, CollectorState state) throws AccessorException, BindingException, HistoryException { Double t = toBeforeTime; Binding timeBinding = null; - CollectorState state = collector != null ? (CollectorState) collector.getState() : null; Bean[] items = history.getItems(); //System.out.println("truncating all samples after t=" + toBeforeTime + " for " + items.length + " history items"); for (Bean item : items) { String id = (String) item.getField("id"); - StreamAccessor sa = history.openStream(id, "w"); + StreamAccessor sa = history.openStream(id, "rw"); try { Stream s = new Stream(sa); timeBinding = s.timeBinding; @@ -972,7 +972,8 @@ public class HistoryUtil { if (state != null) { Object prevTime = newSize > 0 ? s.getItemTime(s.timeBinding, newSize - 1) : null; - Object prevValue = newSize > 0 ? sa.get(newSize - 1, s.valueBinding) : null; + Bean prevSample = newSize > 0 ? (Bean) sa.get(newSize - 1, s.sampleBinding) : null; + Object prevValue = prevSample != null ? prevSample.getField(s.valueIndex) : null; boolean isNan = isNaN(prevValue); VariableState vs = state.values.get(id); @@ -984,18 +985,18 @@ public class HistoryUtil { CollectorState.Item is = state.itemStates.get(id); if (is != null) { - is.firstTime = Double.NaN; - is.firstValue = null; - is.currentTime = toTime(prevTime); - is.currentValue = prevValue != null ? new MutableVariant(s.valueBinding, prevValue) : null; - is.isNaN = isNaN(is.currentValue); - is.isValid = is.currentValue != null; + is.firstTime = toTime(prevTime); + is.firstValue = toValue(s.valueBinding, prevValue); + is.currentTime = is.firstTime; + is.currentValue = toValue(s.valueBinding, prevValue); + is.isNaN = isNan; + is.isValid = prevValue != null; is.sum = Double.NaN; is.count = 0; is.ooDeadband = false; is.firstDisabledTime = Double.NaN; is.lastDisabledTime = Double.NaN; - is.median = null; + is.median = new WeightedMedian( CollectorImpl.MEDIAN_LIMIT ); } } } @@ -1007,14 +1008,19 @@ public class HistoryUtil { if (timeBinding != null && state != null) { state.time.setValue(timeBinding, t); state.dT = 1.0; - collector.setState(state); } + + return state; } private static double toTime(Object time) { return time instanceof Number ? ((Number) time).doubleValue() : Double.NaN; } + private static MutableVariant toValue(Binding valueBinding, Object value) { + return new MutableVariant(valueBinding, value != null ? value : valueBinding.createDefaultUnchecked()); + } + private static boolean isNaN(Object value) { return value instanceof Number ? Double.isNaN(((Number) value).doubleValue()) : false; } -- 2.43.2