/******************************************************************************* * Copyright (c) 2007, 2019 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Semantum Oy - initial API and implementation *******************************************************************************/ package org.simantics.district.network.profile; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.procedure.Listener; import org.simantics.db.request.Read; import org.simantics.diagram.profile.StyleBase; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.scenegraph.INode; import org.simantics.scenegraph.profile.EvaluationContext; import org.simantics.scenegraph.profile.Group; import org.simantics.scenegraph.profile.Observer; import org.simantics.scenegraph.profile.common.ObserverGroupListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class ThrottledStyleBase extends StyleBase> { private static final Logger LOGGER = LoggerFactory.getLogger(ThrottledStyleBase.class); private static final long DEFAULT_INTERVAL = 2000; //private long lastCalculateTimestamp = 0; private AtomicLong interval = new AtomicLong(DEFAULT_INTERVAL); private Listener> resultListener; @Override protected Read> getStyleCalculationRequest(Resource runtimeDiagram, Resource entry, Resource item) { //Simantics.getSession().asyncRequest(new ProfileUpdateIntervalRead(runtimeDiagram), new ProfileUpdateIntervalListener(runtimeDiagram, entry, item)); return super.getStyleCalculationRequest(runtimeDiagram, entry, item); } @Override protected Listener> getStyleResultListener(ObserverGroupListener groupListener, Resource item, Group group, Observer observer, Resource runtimeDiagram) { resultListener = super.getStyleResultListener(groupListener, item, group, observer, runtimeDiagram); return resultListener; } @Override public final Optional calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException { // Needs fixing - this will result registration of listeners for nothing in the cache // long currentTimestamp = System.currentTimeMillis(); // if (lastCalculateTimestamp > (currentTimestamp - interval.get())) { // LOGGER.debug("Throttling result calculation for {} {} {} {}", runtimeDiagram, entry, groupItem, interval.get()); // return Optional.empty(); // } // lastCalculateTimestamp = currentTimestamp; // let's calculate return Optional.ofNullable(calculateThrottledStyle(graph, runtimeDiagram, entry, groupItem)); } public abstract Result calculateThrottledStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource groupItem) throws DatabaseException; @Override public final void applyStyleForNode(EvaluationContext evaluationContext, INode node, Optional result) { if (!Optional.empty().equals(result) && result != null) { applyThrottledStyleForNode(evaluationContext, node, result.get()); } else { LOGGER.debug("Do not apply as results are unchanged for {} {} {}", evaluationContext, node, result); // TODO: fix this duplicate method invocation with null applyThrottledStyleForNode(evaluationContext, node, null); } } public abstract void applyThrottledStyleForNode(EvaluationContext evaluationContext, INode node, Result result); private static class ProfileUpdateIntervalRead extends UnaryRead { public ProfileUpdateIntervalRead(Resource parameter) { super(parameter); } @Override public Long perform(ReadGraph graph) throws DatabaseException { Resource configuration = graph.getPossibleObject(parameter, DiagramResource.getInstance(graph).RuntimeDiagram_HasConfiguration); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); Long interval = DEFAULT_INTERVAL; if (configuration != null) { interval = graph.getPossibleRelatedValue(configuration, DN.Diagram_profileUpdateInterval, Bindings.LONG); } return interval != null ? interval : DEFAULT_INTERVAL; } } private class ProfileUpdateIntervalListener implements Listener { private Resource runtimeDiagram; private Resource entry; private Resource item; public ProfileUpdateIntervalListener(Resource runtimeDiagram, Resource entry, Resource item) { this.runtimeDiagram = runtimeDiagram; this.entry = entry; this.item = item; } @Override public void execute(Long result) { interval.set(result); } @Override public void exception(Throwable t) { LOGGER.error("Could not listen interval from runtime diagram {}", runtimeDiagram, t); } @Override public boolean isDisposed() { return resultListener.isDisposed(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getEnclosingInstance().hashCode(); result = prime * result + ((entry == null) ? 0 : entry.hashCode()); result = prime * result + ((item == null) ? 0 : item.hashCode()); result = prime * result + ((runtimeDiagram == null) ? 0 : runtimeDiagram.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ProfileUpdateIntervalListener other = (ProfileUpdateIntervalListener) obj; if (!getEnclosingInstance().equals(other.getEnclosingInstance())) return false; if (entry == null) { if (other.entry != null) return false; } else if (!entry.equals(other.entry)) return false; if (item == null) { if (other.item != null) return false; } else if (!item.equals(other.item)) return false; if (runtimeDiagram == null) { if (other.runtimeDiagram != null) return false; } else if (!runtimeDiagram.equals(other.runtimeDiagram)) return false; return true; } private ThrottledStyleBase getEnclosingInstance() { return ThrottledStyleBase.this; } } }