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