]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling/src/org/simantics/modeling/subscription/CollectSubscriptions.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / subscription / CollectSubscriptions.java
index 169c246cfda5c15cecccca7e35d3f21f58e25dfa..0bd273fab869de59969a21a97483bffbbf87ad75 100644 (file)
-/*******************************************************************************\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
+/*******************************************************************************
+ * 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<SubscriptionCollectionResult> {
+
+    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<String, SubscriptionItem> items = gatherSubscriptions(graph, resource, status, new TreeMap<String, SubscriptionItem>());
+            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<String, SubscriptionItem> gatherSubscriptions(ReadGraph graph, Resource model, MultiStatus status,
+            Map<String, SubscriptionItem> 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<String, SubscriptionItem> subscriptionItems = graph.syncRequest(
+                    new SubscriptionRequest(subscription, constants),
+                    TransientCacheAsyncListener.<Map<String, SubscriptionItem>>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<Resource, Param, Map<String, SubscriptionItem>> {
+
+        public SubscriptionRequest(Resource subscription, Param constants) {
+            super(subscription, constants);
+        }
+
+        @Override
+        public Map<String, SubscriptionItem> 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<String, SubscriptionItem> items = graph.syncRequest(
+                    new SubscriptionItemsRequest(parameter, groupId, parameter2),
+                    TransientCacheAsyncListener.<Map<String, SubscriptionItem>>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<Resource, String, Param, Map<String, SubscriptionItem>> {
+
+        public SubscriptionItemsRequest(Resource subscription, String groupId, Param constants) {
+            super(subscription, groupId, constants);
+        }
+
+        @Override
+        public Map<String, SubscriptionItem> 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<Resource> subscriptionItems = graph.syncRequest(new ObjectsWithType(parameter, L0.ConsistsOf, MOD.Subscription_Item));
+            if (subscriptionItems.isEmpty())
+                return Collections.emptyMap();
+
+            Map<String, SubscriptionItem> result = new THashMap<>(subscriptionItems.size());
+            for (Resource subscriptionItem : subscriptionItems) {
+                SubscriptionItem hi = graph.syncRequest(
+                        new ItemRequest(subscriptionItem, parameter2, parameter3),
+                        TransientCacheListener.<SubscriptionItem>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<Resource, String, Param, SubscriptionItem> {
+
+        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, Variable> 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;
+        }
+
+    }
+
 }
\ No newline at end of file