L0 = <http://www.simantics.org/Layer0-1.1>
STR = <http://www.simantics.org/Structural-1.2>
+SEL = <http://www.simantics.org/SelectionView-1.2>
DN = <http://www.simantics.org/DistrictNetwork-1.0>
// Link to corresponding component type
>-- table.HasComponentType <R L0.IsRelatedTo : L0.FunctionalRelation
--> STR.ComponentType
+
+TT.Functions : L0.Library
+TT.Functions.techTypeCodeValueAccessor : L0.ExternalValue
+ L0.HasValueType "ValueAccessor"
+
+TT.TechTypeCodeParameterType : SEL.GenericParameterType
+ @L0.assert L0.valueAccessor TT.Functions.techTypeCodeValueAccessor
public final Resource SupplyInConnectionType;
public final Resource SupplyOutConnectionType;
public final Resource TechType;
+ public final Resource TechType_Functions;
+ public final Resource TechType_Functions_techTypeCodeValueAccessor;
+ public final Resource TechType_TechTypeCodeParameterType;
public final Resource TechType_TechTypeTable;
public final Resource TechType_TechTypeTable_HasComponentType;
public final Resource TechType_TechTypeTable_HasData;
public static final String SupplyInConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyInConnectionType";
public static final String SupplyOutConnectionType = "http://www.simantics.org/DistrictNetwork-1.0/SupplyOutConnectionType";
public static final String TechType = "http://www.simantics.org/DistrictNetwork-1.0/TechType";
+ public static final String TechType_Functions = "http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions";
+ public static final String TechType_Functions_techTypeCodeValueAccessor = "http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions/techTypeCodeValueAccessor";
+ public static final String TechType_TechTypeCodeParameterType = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeCodeParameterType";
public static final String TechType_TechTypeTable = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable";
public static final String TechType_TechTypeTable_HasComponentType = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable/HasComponentType";
public static final String TechType_TechTypeTable_HasData = "http://www.simantics.org/DistrictNetwork-1.0/TechType/TechTypeTable/HasData";
SupplyInConnectionType = getResourceOrNull(graph, URIs.SupplyInConnectionType);
SupplyOutConnectionType = getResourceOrNull(graph, URIs.SupplyOutConnectionType);
TechType = getResourceOrNull(graph, URIs.TechType);
+ TechType_Functions = getResourceOrNull(graph, URIs.TechType_Functions);
+ TechType_Functions_techTypeCodeValueAccessor = getResourceOrNull(graph, URIs.TechType_Functions_techTypeCodeValueAccessor);
+ TechType_TechTypeCodeParameterType = getResourceOrNull(graph, URIs.TechType_TechTypeCodeParameterType);
TechType_TechTypeTable = getResourceOrNull(graph, URIs.TechType_TechTypeTable);
TechType_TechTypeTable_HasComponentType = getResourceOrNull(graph, URIs.TechType_TechTypeTable_HasComponentType);
TechType_TechTypeTable_HasData = getResourceOrNull(graph, URIs.TechType_TechTypeTable_HasData);
import org.simantics.Simantics;
import org.simantics.db.Resource;
import org.simantics.db.exception.DatabaseException;
-import org.simantics.district.network.techtype.requests.WriteTechTypeTableRequest;
+import org.simantics.district.network.techtype.requests.WriteTechTypeTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
try {
- Simantics.getSession().syncRequest(new WriteTechTypeTableRequest(componentType, data));
+ Simantics.getSession().syncRequest(new WriteTechTypeTable(componentType, data));
} catch (DatabaseException e) {
LOGGER.error("Failed to write tech type table data to model", e);
}
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Simantics District Network
-Bundle-SymbolicName: org.simantics.district.network
+Bundle-SymbolicName: org.simantics.district.network;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-11
Require-Bundle: org.simantics.db,
org.simantics.db.indexing,
org.simantics.scl.osgi,
org.eclipse.collections,
- org.simantics.structural.synchronization.client
+ org.simantics.structural.synchronization.client,
+ org.apache.commons.csv
Export-Package: org.simantics.district.network,
org.simantics.district.network.changeset,
org.simantics.district.network.profile,
+ org.simantics.district.network.techtype,
org.simantics.district.network.techtype.requests,
+ org.simantics.district.network.techtype.variable,
org.simantics.district.network.visualisations,
org.simantics.district.network.visualisations.model,
org.simantics.district.network.visualisations.triggers
bin.includes = META-INF/,\
.,\
adapters.xml,\
- scl/
+ scl/,\
+ plugin.xml
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.simantics.scl.reflection.binding">
+ <namespace path="http://www.simantics.org/DistrictNetwork-1.0/TechType/Functions">
+ <externalClass className="org.simantics.db.layer0.variable.ValueAccessor"/>
+ <class className="org.simantics.district.network.techtype.variable.Functions"/>
+ </namespace>
+ </extension>
+
+</plugin>
--- /dev/null
+import "Simantics/DB"
+
+importJava "org.simantics.district.network.techtype.TechTypeUtils" where
+ "Update component properties from tech type table: `updateComponent component`"
+ updateComponent :: Resource -> <Proc> ()
+ "Reset all components to values in a tech type table: `resetComponents table`"
+ resetComponents :: Resource -> <Proc> ()
--- /dev/null
+package org.simantics.district.network.techtype;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.binding.NumberBinding;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.NumberType;
+import org.simantics.databoard.type.StringType;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.QueryIndexUtils;
+import org.simantics.db.layer0.request.PossibleVariable;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.techtype.requests.PossibleTechTypeItem;
+import org.simantics.district.network.techtype.requests.PossibleTechTypeTable;
+import org.simantics.district.network.techtype.requests.TechTypeTableKeyName;
+import org.simantics.scl.runtime.SCLContext;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeUtils {
+
+ final static Logger LOGGER = LoggerFactory.getLogger(TechTypeUtils.class);
+
+ public static String DEFAULT_KEY_NAME = "pipeCode";
+
+ /**
+ * Execute a cached query for a possible tech type table item in a table with a given item key.
+ *
+ * Result is null, if no such item was found.
+ *
+ * @param session A request processor on which to run the query
+ * @param itemCode A key value, such as a pipe code
+ * @throws DatabaseException
+ */
+ public static Map<String, String> getTableItem(RequestProcessor session, Resource table, String itemCode) throws DatabaseException {
+ return session.syncRequest(new PossibleTechTypeItem(table, itemCode), TransientCacheListener.instance());
+ }
+
+ /**
+ * Get a single row in a tech type table for a given item code
+ *
+ * Result is null, if no such item was found.
+ *
+ * @param table A TechTypeTable resource
+ * @param itemCode A key value, such as a pipe code
+ * @return A map from property name to value
+ * @throws DatabaseException
+ */
+ public static Map<String, String> getTableItem(Resource table, String itemCode) throws DatabaseException {
+ Object graph = SCLContext.getCurrent().get("graph");
+ if (graph != null && graph instanceof ReadGraph)
+ return getTableItem((ReadGraph) graph, table, itemCode);
+ else
+ return getTableItem(Simantics.getSession(), table, itemCode);
+ }
+
+ /**
+ * Reset all components that address the given tech type table to the table values.
+ *
+ * @param table A tech type table
+ * @throws DatabaseException
+ */
+ public static void resetComponents(Resource table) throws DatabaseException {
+ Simantics.getSession().syncRequest(new WriteRequest() {
+ @Override
+ public void perform(WriteGraph graph) throws DatabaseException {
+ Resource model = graph.syncRequest(new PossibleIndexRoot(table), TransientCacheListener.instance());
+ if (model == null)
+ return;
+
+ Resource type = graph.getPossibleObject(table, DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasComponentType);
+ if (type == null)
+ return;
+
+ Collection<Resource> components = QueryIndexUtils.searchByType(graph, model, type);
+ for (Resource component : components) {
+ updateComponent(graph, component, table);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update property values of a component based on the values in an associated tech type table, if any
+ *
+ * @param graph A write access interface to the graph database
+ * @param component A structural component
+ * @throws DatabaseException
+ */
+ public static void updateComponent(WriteGraph graph, Resource component) throws DatabaseException {
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);
+
+ Resource type = graph.getSingleType(component, STR.Component);
+ Resource model = graph.syncRequest(new PossibleIndexRoot(component));
+ if (model == null) {
+ LOGGER.info("updateComponent: No model for {}", component);
+ return;
+ }
+
+ Resource table = graph.syncRequest(new PossibleTechTypeTable(model, type), TransientCacheListener.instance());
+ if (table == null) {
+ LOGGER.info("updateComponent: No tech type table for {} in {}", type, model);
+ return;
+ }
+
+ updateComponent(graph, component, table);
+ }
+
+ /**
+ * Update property values of a component based on the values in an associated tech type table, if any
+ *
+ * @param component A structural component
+ * @throws DatabaseException
+ */
+ public static void updateComponent(Resource component) throws DatabaseException {
+ if (LOGGER.isTraceEnabled())
+ LOGGER.trace("updateComponent({})", component);
+
+ Simantics.getSession().syncRequest(new WriteRequest() {
+ @Override
+ public void perform(WriteGraph graph) throws DatabaseException {
+ updateComponent(graph, component);
+ }
+ });
+ }
+
+ /**
+ * Update a single component's property values to those selected from a tech
+ * type table
+ *
+ * @param graph A write interface to the db
+ * @param component The component to be updated
+ * @param table A tech type table
+ * @throws DatabaseException
+ */
+ public static void updateComponent(WriteGraph graph, Resource component, Resource table)
+ throws DatabaseException {
+ if (LOGGER.isTraceEnabled())
+ LOGGER.trace("updateComponent(graph, {}, {}), component, table)");
+
+ Variable v = graph.syncRequest(new PossibleVariable(component));
+ if (v == null) {
+ LOGGER.info("No variable found for {}", component);
+ return;
+ }
+
+ String keyProp = getKeyPropertyName(graph, table);
+ String itemCode = v.getPropertyValue(graph, keyProp);
+
+ Map<String, String> map = graph.syncRequest(new PossibleTechTypeItem(table, itemCode), TransientCacheListener.instance());
+ if (map == null) {
+ LOGGER.info("No entry found for \"{}\" in tech type table {}", itemCode, table);
+ return;
+ }
+
+ for (String key : map.keySet()) {
+ // Don't write the key property
+ if (key.equals(DEFAULT_KEY_NAME))
+ continue;
+
+ Variable prop = v.getPossibleProperty(graph, key);
+ if (prop == null) continue;
+
+ Datatype dt = prop.getPossibleDatatype(graph);
+ if (dt == null) continue;
+
+ String value = map.get(key);
+ if (dt instanceof NumberType) {
+ try {
+ Double num = Double.valueOf(value.replace(",", "."));
+ NumberBinding binding = Bindings.getBinding(dt);
+ prop.setValue(graph, binding.create(num));
+ } catch (NumberFormatException e) {
+ // Revert to asserted value
+ Resource pred = prop.getPossiblePredicateResource(graph);
+ if (pred != null)
+ graph.deny(component, pred);
+ } catch (BindingException e) {
+ LOGGER.error("Failed to get binding for datatype {}", dt, e);
+ }
+ } else if (dt instanceof StringType) {
+ prop.setValue(graph, value);
+ } else {
+ LOGGER.warn("updateComponent: Unsupported property type {}", dt);
+ }
+ }
+ }
+
+ public static String getKeyPropertyName(ReadGraph graph, Resource table) throws DatabaseException {
+ String name = graph.syncRequest(new TechTypeTableKeyName(table), TransientCacheListener.instance());
+ if (name == null) name = "_" + DEFAULT_KEY_NAME;
+ return name;
+ }
+}
--- /dev/null
+package org.simantics.district.network.techtype.requests;
+
+import java.util.Map;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.BinaryRead;
+import org.simantics.db.exception.DatabaseException;
+
+/**
+ * Query for a possible tech type table item in a table with a given item key.
+ *
+ * Results in null, if no such item was found.
+ */
+public class PossibleTechTypeItem extends BinaryRead<Resource, String, Map<String, String>> {
+
+ public PossibleTechTypeItem(Resource table, String itemCode) {
+ super(table, itemCode);
+ }
+
+ @Override
+ public Map<String, String> perform(ReadGraph graph) throws DatabaseException {
+ Resource table = this.parameter;
+ String itemCode = this.parameter2;
+
+ Map<String, Map<String, String>> map = graph.syncRequest(new TechTypeTableData(table), TransientCacheListener.instance());
+ return map.get(itemCode);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.district.network.techtype.requests;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.techtype.TechTypeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TechTypeTableData extends ResourceRead<Map<String, Map<String, String>>> {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(TechTypeTableData.class);
+
+ protected TechTypeTableData(Resource table) {
+ super(table);
+ }
+
+ @Override
+ public Map<String, Map<String, String>> perform(ReadGraph graph) throws DatabaseException {
+ Resource table = this.resource;
+
+ String data = graph.getRelatedValue2(table,
+ DistrictNetworkResource.getInstance(graph).TechType_TechTypeTable_HasData);
+ if (data == null)
+ return Collections.emptyMap();
+
+ String keyName = TechTypeUtils.getKeyPropertyName(graph, table);
+ if (keyName.startsWith("_"))
+ keyName = keyName.substring(1);
+
+ Map<String, Map<String, String>> map = new HashMap<>();
+
+ long ncommas = data.chars().filter(c -> c == ',').count();
+ long nsemis = data.chars().filter(c -> c == ';').count();
+ char delim = nsemis > ncommas ? ';' : ',';
+ StringReader reader = new StringReader(data);
+
+ List<CSVRecord> records = new ArrayList<>();
+ try {
+ CSVFormat format = CSVFormat.newFormat(delim).withQuote('"');
+ try (CSVParser parser = format.parse(reader)) {
+ Iterator<CSVRecord> it = parser.iterator();
+ while (it.hasNext()) {
+ records.add(it.next());
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error("Error reading CSV data", e);
+ return Collections.emptyMap();
+ }
+
+ CSVRecord header = records.remove(0);
+ records.remove(0); // Eliminate units row
+
+ int keyIndex = 0;
+ for (int i = 0; i < header.size(); i++) {
+ if (header.get(i).equals(keyName)) {
+ keyIndex = i;
+ break;
+ }
+ }
+
+ for (CSVRecord r : records) {
+ String key = r.get(keyIndex).intern();
+ if (key == null)
+ continue;
+
+ Map<String, String> valueMap = new HashMap<>();
+ map.put(key, valueMap);
+
+ Iterator<String> h = header.iterator();
+ Iterator<String> v = r.iterator();
+
+ while (h.hasNext() && v.hasNext()) {
+ String name = h.next().intern();
+ String value = v.next();
+ valueMap.put(name, value);
+ }
+ }
+
+ return map;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.district.network.techtype.requests;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.layer0.Layer0;
+
+public class TechTypeTableKeyName extends ResourceRead<String> {
+
+ public TechTypeTableKeyName(Resource table) {
+ super(table);
+ }
+
+ @Override
+ public String perform(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+
+ Resource type = graph.getPossibleObject(resource, DN.TechType_TechTypeTable_HasComponentType);
+ if (type != null) {
+ for (Resource r : graph.getObjects(type, L0.DomainOf)) {
+ Resource accessor = graph.getPossibleObject(r, L0.valueAccessor);
+ if (accessor.equals(DN.TechType_Functions_techTypeCodeValueAccessor)) {
+ return graph.getRelatedValue2(r, L0.HasName);
+ }
+ }
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
import org.simantics.district.network.ontology.DistrictNetworkResource;
import org.simantics.layer0.Layer0;
-public final class WriteTechTypeTableRequest extends WriteRequest {
+public final class WriteTechTypeTable extends WriteRequest {
private final String data;
private final Resource componentType;
- public WriteTechTypeTableRequest(Resource componentType, String data) {
+ public WriteTechTypeTable(Resource componentType, String data) {
this.data = data;
this.componentType = componentType;
}
--- /dev/null
+package org.simantics.district.network.techtype.variable;
+
+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.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.ValueAccessor;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.district.network.techtype.TechTypeUtils;
+import org.simantics.scl.reflection.annotations.SCLValue;
+import org.simantics.structural.stubs.StructuralResource2;
+
+public class Functions {
+ @SCLValue(type = "ValueAccessor")
+ public static final ValueAccessor techTypeCodeValueAccessor = new ValueAccessor() {
+
+ @Override
+ public Object getValue(ReadGraph graph, Variable context) throws DatabaseException {
+ return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context);
+ }
+
+ @Override
+ public Object getValue(ReadGraph graph, Variable context, Binding binding) throws DatabaseException {
+ return org.simantics.db.layer0.function.All.standardValueAccessor.getValue(graph, context, binding);
+ }
+
+ @Override
+ public void setValue(WriteGraph graph, Variable context, Object value) throws DatabaseException {
+ org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, value);
+
+ updateComponentProperties(graph, context, value);
+ }
+
+ @Override
+ public void setValue(WriteGraph graph, Variable context, Object value, Binding binding)
+ throws DatabaseException {
+ org.simantics.db.layer0.function.All.standardValueAccessor.setValue(graph, context, value, binding);
+
+ updateComponentProperties(graph, context, value);
+ }
+
+ @Override
+ public Datatype getDatatype(ReadGraph graph, Variable context) throws DatabaseException {
+ return org.simantics.db.layer0.function.All.standardValueAccessor.getDatatype(graph, context);
+ }
+
+ };
+
+ protected static void updateComponentProperties(WriteGraph graph, Variable context, Object value) throws DatabaseException {
+ Variable component = context.getParent(graph);
+ Resource type = component.getPossibleType(graph, StructuralResource2.getInstance(graph).Component);
+ if (type == null)
+ return;
+
+ TechTypeUtils.updateComponent(graph, component.getRepresents(graph));
+ }
+}