/******************************************************************************* * Copyright (c) 2011 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.subscription; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.TreeMap; import org.eclipse.core.runtime.MultiStatus; import org.simantics.databoard.Bindings; import org.simantics.databoard.Databoard; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.type.Datatype; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.common.request.BinaryRead; import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.common.request.ResourceRead; import org.simantics.db.common.request.TernaryRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.RVI; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.history.util.subscription.SubscriptionItem; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.scl.runtime.tuple.Tuple3; import org.simantics.simulation.experiment.IDynamicExperiment; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.utils.datastructures.Pair; import gnu.trove.map.hash.THashMap; /** * @author Tuukka Lehtonen */ public class CollectSubscriptions extends ResourceRead { private static final boolean PERF = false; private static final boolean DEBUG = false; protected Resource experiment; protected double defaultSamplingInterval; protected boolean synchronous; public CollectSubscriptions(IDynamicExperiment experiment, double defaultSamplingInterval) { this(experiment.getModel(), experiment.getResource(), defaultSamplingInterval, false); } public CollectSubscriptions(IDynamicExperiment experiment, double defaultSamplingInterval, boolean synchronous) { this(experiment.getModel(), experiment.getResource(), defaultSamplingInterval, synchronous); } public CollectSubscriptions(Resource model, Resource experiment, double defaultSamplingInterval) { this(model, experiment, defaultSamplingInterval, false); } public CollectSubscriptions(Resource model, Resource experiment, double defaultSamplingInterval, boolean synchronous) { super(model); this.experiment = experiment; this.defaultSamplingInterval = defaultSamplingInterval; } @Override public SubscriptionCollectionResult perform(ReadGraph graph) throws DatabaseException { MultiStatus status = new MultiStatus(ModelHistoryCollector.BUNDLE_ID, 0, "History collection subscription resolution problems:", null); boolean oldSync = graph.setSynchronous(synchronous); try { Map items = gatherSubscriptions(graph, resource, status, new TreeMap()); return new SubscriptionCollectionResult(new ArrayList<>(items.values()), status); } finally { graph.setSynchronous(oldSync); } } /** * Scans thru a model and writes up all the variables that are to be monitored. * This includes: * Subscriptions * Charts * Spreadsheets (not in cur. impl) * Monitors on diagram (not in cur. impl) * * @param graph * @param model * @param status * @param variablesToMonitor * @throws DatabaseException */ public Map gatherSubscriptions(ReadGraph graph, Resource model, MultiStatus status, Map items) throws DatabaseException { if (PERF) System.out.println("DEBUG: CollectAprosSubscriptions.gatherSubscriptions"); long start = System.nanoTime(); // Get active experiment context if possible to be able // to resolve the whole model, not just the configuration. Variable configuration = Variables.getPossibleConfigurationContext(graph, model); if (configuration == null) return items; Variable experimentVariable = null; SimulationResource SIMU = SimulationResource.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); for (Resource run : graph.syncRequest(new ObjectsWithType(experiment, L0.ConsistsOf, SIMU.Run))) { if (graph.hasStatement(run, SIMU.IsActive)) { try { experimentVariable = Variables.switchRealization(graph, configuration, run); } catch (DatabaseException e) { experimentVariable = Variables.switchPossibleContext(graph, configuration, run); } } } ModelingResources MOD = ModelingResources.getInstance(graph); Param constants = new Param(configuration, experimentVariable, defaultSamplingInterval); for (Resource subscription : graph.syncRequest(new ObjectsWithType(model, L0.ConsistsOf, MOD.Subscription))) { Map subscriptionItems = graph.syncRequest( new SubscriptionRequest(subscription, constants), TransientCacheAsyncListener.>instance()); items.putAll(subscriptionItems); } if (PERF) System.out.println("DEBUG: CollectAprosSubscriptions.gatherSubscriptions ENDS, took " + ((System.nanoTime() - start)*1e-6) + " ms with " + items.size() + " items"); return items; } static class Param extends Tuple3 { public Param(Variable configurationContext, Variable experimentContext, Double defaultSamplingInterval) { super(configurationContext, experimentContext, defaultSamplingInterval); } } static class SubscriptionRequest extends BinaryRead> { public SubscriptionRequest(Resource subscription, Param constants) { super(subscription, constants); } @Override public Map perform(ReadGraph graph) throws DatabaseException { if (PERF) System.out.println("DEBUG: CollectSubscriptions.SubscriptionRequest(" + NameUtils.getSafeName(graph, parameter, true) + ")"); long start = System.nanoTime(); ModelingResources MOD = ModelingResources.getInstance(graph); Boolean v = graph.getPossibleRelatedValue(parameter, MOD.Subscription_Enabled, Bindings.BOOLEAN); if (!Boolean.TRUE.equals(v)) return Collections.emptyMap(); Layer0 L0 = Layer0.getInstance(graph); String groupId = graph.getPossibleRelatedValue(parameter, L0.HasName, Bindings.STRING); if (groupId == null) return Collections.emptyMap(); Map items = graph.syncRequest( new SubscriptionItemsRequest(parameter, groupId, parameter2), TransientCacheAsyncListener.>instance()); if (PERF) System.out.println("DEBUG: CollectSubscriptions.SubscriptionRequest(" + NameUtils.getSafeName(graph, parameter, true) + ") DONE in " + ((System.nanoTime() - start)*1e-6) + " ms"); return items; } } static class SubscriptionItemsRequest extends TernaryRead> { public SubscriptionItemsRequest(Resource subscription, String groupId, Param constants) { super(subscription, groupId, constants); } @Override public Map perform(ReadGraph graph) throws DatabaseException { if (PERF) System.out.println("DEBUG: CollectSubscriptions.SubscriptionItemsRequest(" + NameUtils.getSafeName(graph, parameter, true) + ")"); long start = System.nanoTime(); Layer0 L0 = Layer0.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); Collection subscriptionItems = graph.syncRequest(new ObjectsWithType(parameter, L0.ConsistsOf, MOD.Subscription_Item)); if (subscriptionItems.isEmpty()) return Collections.emptyMap(); Map result = new THashMap<>(subscriptionItems.size()); for (Resource subscriptionItem : subscriptionItems) { SubscriptionItem hi = graph.syncRequest( new ItemRequest(subscriptionItem, parameter2, parameter3), TransientCacheListener.instance()); if (hi != null) result.put(hi.id, hi); } if (PERF) System.out.println("DEBUG: CollectSubscriptions.SubscriptionItemsRequest(" + NameUtils.getSafeName(graph, parameter, true) + ") DONE in " + ((System.nanoTime() - start)*1e-6) + " ms with " + result.size() + " items"); return result; } } static class ItemRequest extends TernaryRead { public ItemRequest(Resource item, String groupId, Param constants) { super(item, groupId, constants); } @Override public SubscriptionItem perform(ReadGraph graph) throws DatabaseException { ModelingResources MOD = ModelingResources.getInstance(graph); Resource subscriptionItem = parameter; String groupId = parameter2; Param constants = parameter3; Variable configurationContext = (Variable) constants.c0; Variable experimentContext = (Variable) constants.c1; double defaultSamplingInterval = (double) constants.c2; Binding rviBinding = graph.getService(Databoard.class).getBindingUnchecked( RVI.class ); RVI rvi = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_VariableId, rviBinding); if (rvi == null) { //status.add(new Status(IStatus.WARNING, ModelHistoryCollector.BUNDLE_ID, "Subscription Item '" + NameUtils.getSafeName(graph, subscriptionItem) + "' is missing RVI property")); return null; } Layer0 L0 = Layer0.getInstance(graph); String guid = graph.getPossibleRelatedValue(subscriptionItem, L0.HasName); if (guid == null) { //status.add(new Status(IStatus.WARNING, ModelHistoryCollector.BUNDLE_ID, "Subscription Item '" + NameUtils.getSafeName(graph, subscriptionItem) + "' has no name (ID).")); return null; } String variablePersistentId = rvi.toString(); Pair variable = Variables.resolvePossible(graph, rvi, configurationContext, experimentContext); if (variable == null) { if (DEBUG) System.out.println("DEBUG: unresolvable subscription: " + variablePersistentId); // Don't log these, these problems are conveyed to the // user through model browser UI labels. //status.add(new Status(IStatus.WARNING, ModelHistoryCollector.BUNDLE_ID, "Subscription Item '" + NameUtils.getSafeName(graph, subscriptionItem) + "' variable cannot be resolved from RVI " + rvi.toString(graph))); return null; } String variableId = rvi.asPossibleString(graph, variable.second); //System.out.println("DEBUG: " + variablePersistentId + " - " + variableId); if (variableId == null) { return null; } Datatype type = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_Datatype, Bindings.getBindingUnchecked(Datatype.class)); if (type == null && variable != null) { type = variable.first.getPossibleDatatype(graph); } if (type == null) { //status.add(new Status(IStatus.WARNING, ModelHistoryCollector.BUNDLE_ID, "Subscription Item '" + NameUtils.getSafeName(graph, subscriptionItem) + "' is missing data type")); // Can't function without a datatype. return null; } Double itemSamplingInterval = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_SamplingInterval, Bindings.DOUBLE); Double itemDeadband = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_Deadband, Bindings.DOUBLE); Double bias = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_Bias, Bindings.DOUBLE); Double gain = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_Gain, Bindings.DOUBLE); String unit = graph.getPossibleRelatedValue(subscriptionItem, MOD.Subscription_Item_Unit, Bindings.STRING); double samplingInterval = itemSamplingInterval != null ? itemSamplingInterval : defaultSamplingInterval; double deadband = itemDeadband != null ? itemDeadband : 0.0; SubscriptionItem item = new SubscriptionItem(); // HACK: use id for storing the name of the item // Assumption: subscription item's name is unique within the model item.id = guid; item.variableId = variableId; item.groupId = groupId; item.groupItemId = variablePersistentId; // HACK: use format for storing the data type of the item item.format = type; // HACK: use formatId for storing the unit of the item, // empty unit is interpreted to mean "no unit" because // formatId cannot be null. item.formatId = unit == null ? "" : unit; // Subscription parameters item.deadband = deadband; item.interval = samplingInterval; if (bias != null) item.bias = bias; if (gain != null) item.gain = gain; if (DEBUG) //System.out.println("DEBUG: ItemRequest(" + parameter + ", " + parameter2.getURI(graph) + ", " + parameter3 + ")\n\t= " + item); System.out.println("DEBUG: ItemRequest(" + parameter + ", " + parameter2 + ", " + parameter3 + ")\n\t= " + item); return item; } } }