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
*******************************************************************************/
package org.simantics.charts.editor;
*******************************************************************************/
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;
import org.simantics.databoard.annotations.Optional;
import org.simantics.db.Resource;
import org.simantics.history.Collector;
@Optional
public Collector collector;
@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;
public ChartData(Resource model, Resource run, IExperiment experiment, Datasource datasource, HistoryManager history, Collector collector) {
this.model = model;
this.run = run;
this.datasource = null;
this.history = null;
this.collector = null;
this.datasource = null;
this.history = null;
this.collector = null;
} else {
this.model = other.model;
this.run = other.run;
} else {
this.model = other.model;
this.run = other.run;
this.datasource = other.datasource;
this.history = other.history;
this.collector = other.collector;
this.datasource = other.datasource;
this.history = other.history;
this.collector = other.collector;
+ this.refCount = other.refCount;
+ 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();
+ }
+ }
+ }
+
// Track data source and preinitialize chartData
project.addHintListener(chartDataListener);
chartData.readFrom( (ChartData) project.getHint( chartDataKey ) );
// Track data source and preinitialize chartData
project.addHintListener(chartDataListener);
chartData.readFrom( (ChartData) project.getHint( chartDataKey ) );
if (chartData.run != null) {
milestoneListener = new MilestoneSpecListener();
if (chartData.run != null) {
milestoneListener = new MilestoneSpecListener();
chartData.datasource.removeListener( stepListener );
if (chartData.experiment!=null)
chartData.experiment.removeListener( experimentStateListener );
chartData.datasource.removeListener( stepListener );
if (chartData.experiment!=null)
chartData.experiment.removeListener( experimentStateListener );
+ chartData.dereference();
chartData.readFrom( null );
}
chartData.readFrom( null );
}
}
if (doLayout) trendNode.layout();
}
if (doLayout) trendNode.layout();
+ this.chartData.dereference();
this.chartData.readFrom( data );
this.chartData.readFrom( data );
+ this.chartData.reference();
tp.setDirty();
if (!ObjectUtils.objectEquals(oldExperimentResource, newExperimentResource)) {
tp.setDirty();
if (!ObjectUtils.objectEquals(oldExperimentResource, newExperimentResource)) {
- @SuppressWarnings("rawtypes")
+ @SuppressWarnings("unchecked")
- public Object getAdapter(Class adapter) {
+ public <T> T getAdapter(Class<T> adapter) {
if (adapter == INode.class) {
ICanvasContext ctx = cvsCtx;
if (ctx != null)
if (adapter == INode.class) {
ICanvasContext ctx = cvsCtx;
if (ctx != null)
- return ctx.getSceneGraph();
+ return (T) ctx.getSceneGraph();
}
if (adapter == IPropertyPage.class)
}
if (adapter == IPropertyPage.class)
- return new StandardPropertyPage(getSite(), getPropertyPageContexts());
+ return (T) new StandardPropertyPage(getSite(), getPropertyPageContexts());
if (adapter == ICanvasContext.class)
if (adapter == ICanvasContext.class)
return super.getAdapter(adapter);
}
return super.getAdapter(adapter);
}
public static class VariableState {
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 */
public MutableVariant value;
/** Set to true if is valid value */
import org.simantics.db.exception.ServiceNotFoundException;
import org.simantics.db.request.Read;
import org.simantics.fastlz.FastLZ;
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.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.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;
import org.simantics.layer0.Layer0;
import org.simantics.simulation.Activator;
import org.simantics.simulation.ontology.HistoryResource;
- 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;
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");
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;
try {
Stream s = new Stream(sa);
timeBinding = s.timeBinding;
if (state != null) {
Object prevTime = newSize > 0 ? s.getItemTime(s.timeBinding, newSize - 1) : null;
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);
boolean isNan = isNaN(prevValue);
VariableState vs = state.values.get(id);
CollectorState.Item is = state.itemStates.get(id);
if (is != null) {
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.sum = Double.NaN;
is.count = 0;
is.ooDeadband = false;
is.firstDisabledTime = Double.NaN;
is.lastDisabledTime = Double.NaN;
+ is.median = new WeightedMedian( CollectorImpl.MEDIAN_LIMIT );
if (timeBinding != null && state != null) {
state.time.setValue(timeBinding, t);
state.dT = 1.0;
if (timeBinding != null && state != null) {
state.time.setValue(timeBinding, t);
state.dT = 1.0;
- collector.setState(state);
}
private static double toTime(Object time) {
return time instanceof Number ? ((Number) time).doubleValue() : Double.NaN;
}
}
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;
}
private static boolean isNaN(Object value) {
return value instanceof Number ? Double.isNaN(((Number) value).doubleValue()) : false;
}