package org.simantics.district.network.ui.function; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.ui.PlatformUI; import org.simantics.NameLabelUtil; import org.simantics.Simantics; import org.simantics.browsing.ui.common.modifiers.EnumeratedValue; import org.simantics.browsing.ui.common.modifiers.Enumeration; import org.simantics.browsing.ui.graph.impl.GraphEnumerationModifier; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.common.request.IndexRoot; import org.simantics.db.common.request.ObjectsWithType; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.layer0.QueryIndexUtils; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.layer0.variable.Variables.Role; import org.simantics.db.procedure.Procedure; import org.simantics.db.request.WriteResult; import org.simantics.district.network.DistrictNetworkUtil; import org.simantics.district.network.ontology.DistrictNetworkResource; import org.simantics.layer0.Layer0; import org.simantics.modeling.typicals.TypicalUtil; import org.simantics.scl.compiler.commands.CommandSession; import org.simantics.scl.compiler.commands.CommandSessionImportEntry; import org.simantics.scl.compiler.errors.CompilationError; import org.simantics.scl.osgi.SCLOsgi; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.FunctionImpl1; import org.simantics.scl.runtime.reporting.SCLReportingHandler; import org.simantics.ui.workbench.action.DefaultActions; import org.simantics.utils.ui.SWTUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Functions { private static final Logger LOGGER = LoggerFactory.getLogger(Functions.class); private Functions() { } private static class HasMappingEnumerationModifier extends GraphEnumerationModifier { public HasMappingEnumerationModifier(Session session, Resource subject, Resource relation, Enumeration enumeration, Resource value) { super(session, subject, relation, enumeration, value); } } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object defaultEdgeMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { Resource diagram = resolveElement(graph, context); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); return baseMappingModifier(graph, diagram, DN.EdgeDefaultMapping, DN.Mapping_EdgeMapping, context); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object defaultVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { Resource diagram = resolveElement(graph, context); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); return baseMappingModifier(graph, diagram, DN.VertexDefaultMapping, DN.Mapping_VertexMapping, context); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object rightClickVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { Resource diagram = resolveElement(graph, context); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); return baseMappingModifier(graph, diagram, DN.RightClickDefaultMapping, DN.Mapping_VertexMapping, context); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object leftClickVertexMappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { Resource diagram = resolveElement(graph, context); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); return baseMappingModifier(graph, diagram, DN.LeftClickDefaultMapping, DN.Mapping_VertexMapping, context); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object mappingModifier(ReadGraph graph, Resource resource, final Variable context) throws DatabaseException { Resource element = resolveElement(graph, context); Resource mappingType = resolveMappingType(graph, element); return baseMappingModifier(graph, element, DistrictNetworkResource.getInstance(graph).HasMapping, mappingType, context); } public static Map getVertexMappings(ReadGraph graph, Resource indexRoot) throws DatabaseException { Map second = getNetworkMappingsByType(graph, indexRoot, DistrictNetworkResource.getInstance(graph).Mapping_VertexMapping); return second; } public static Map getEdgeMappings(ReadGraph graph, Resource indexRoot) throws DatabaseException { Map second = getNetworkMappingsByType(graph, indexRoot, DistrictNetworkResource.getInstance(graph).Mapping_EdgeMapping); return second; } public static Map getCRSs(ReadGraph graph, Resource resource) throws DatabaseException { Map result = getNetworkMappingsByType(graph, graph.sync(new IndexRoot(resource)), DistrictNetworkResource.getInstance(graph).SpatialRefSystem); return result; } public static Map getNetworkMappingsByType(ReadGraph graph, Resource indexRoot, Resource mappingType) throws DatabaseException { List mappings = QueryIndexUtils.searchByType(graph, indexRoot, mappingType); Map result = new HashMap<>(mappings.size()); Layer0 L0 = Layer0.getInstance(graph); mappings.forEach(mapping -> { try { String name = graph.getRelatedValue2(mapping, L0.HasName); Resource existing = result.put(name, mapping); if (existing != null) { LOGGER.warn("Duplicate mapping name! {} {} and existing is {}", name, mapping, existing); } } catch (DatabaseException e) { e.printStackTrace(); } }); return result; } private static Object baseMappingModifier(ReadGraph graph, Resource element, Resource property, Resource mappingType, Variable context) throws DatabaseException { Resource indexRoot = graph.sync(new IndexRoot(element)); List mappings = QueryIndexUtils.searchByType(graph, indexRoot, mappingType); Enumeration enums = Enumeration .make(mappings.stream().map(m -> createEnumeratedValue(graph, m)).collect(Collectors.toList())); Resource currentMapping = graph.getSingleObject(element, property); return new HasMappingEnumerationModifier(Simantics.getSession(), element, property, enums, currentMapping); } private static Resource resolveMappingType(ReadGraph graph, Resource element) throws DatabaseException { DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); if (graph.isInstanceOf(element, DN.Edge)) return DN.Mapping_EdgeMapping; else if (graph.isInstanceOf(element, DN.Vertex)) return DN.Mapping_VertexMapping; throw new IllegalStateException("No mapping type found for element " + element + " : " + graph.getPossibleURI(element)); } private static Resource resolveElement(ReadGraph graph, Variable variable) throws DatabaseException { Role role = variable.getPossibleRole(graph); if (role.equals(Role.PROPERTY)) return resolveElement(graph, variable.getParent(graph)); else return variable.getRepresents(graph); } private static EnumeratedValue createEnumeratedValue(ReadGraph graph, Resource resource) { try { String label = NameLabelUtil.modalName(graph, resource); return new EnumeratedValue(label, resource); } catch (DatabaseException e) { throw new RuntimeDatabaseException(e); } } @SCLValue(type = "ReadGraph -> Resource -> a -> b") public static Object enumerationValues(ReadGraph graph, Resource resource, Object context) throws DatabaseException { //Variable var = (Variable) context; return Collections.emptyList(); } @SCLValue(type = "ReadGraph -> Resource -> a -> b") public static Object convertToValue(ReadGraph graph, Resource resource, Object context) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); String label = graph.getPossibleRelatedValue2(resource, L0.HasLabel, Bindings.STRING); if (label == null) label = graph.getRelatedValue(resource, L0.HasName, Bindings.STRING); return label; } @SCLValue(type = "Resource -> String -> Resource -> Resource") public static Resource compositeInstantiator(final Resource compositeType, final String defaultName, final Resource target) throws DatabaseException { return TypicalUtil.syncExec(procedure -> { if (!SWTUtils.asyncExec(PlatformUI.getWorkbench().getDisplay(), () -> { try { queryInitialValuesAndCreateComposite(compositeType, target, defaultName, procedure); } catch (Throwable t) { procedure.exception(t); } })) { procedure.execute(null); } }); } public static void queryInitialValuesAndCreateComposite(final Resource compositeType, final Resource target, String defaultName, final Procedure procedure) { DefaultMappingsDialog dialog = new DefaultMappingsDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), target); if (dialog.open() != Dialog.OK) { procedure.execute(null); return; } Simantics.getSession().asyncRequest((WriteResult) graph -> { return DistrictNetworkUtil.createNetworkDiagram(graph, target, compositeType, defaultName, dialog.getDefaultEdgeMapping(), dialog.getDefaultVertexMapping(), dialog.getRightClickVertexMapping(), dialog.getLeftClickVertexMapping(), dialog.getCRS() ); }, new Procedure() { @Override public void execute(Resource composite) { DefaultActions.asyncPerformDefaultAction(Simantics.getSession(), composite, false, false, true); procedure.execute(composite); } @Override public void exception(Throwable t) { LOGGER.error("Failed to create composite, see exception for details.", t); procedure.exception(t); } }); } public static Collection getDistrictDiagrams(ReadGraph graph) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Collection indexRoots = graph.sync(new ObjectsWithType(Simantics.getProjectResource(), L0.ConsistsOf, L0.IndexRoot)); DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph); Set results = new HashSet<>(); for (Resource indexRoot : indexRoots) { Collection diagrams = QueryIndexUtils.searchByType(graph, indexRoot, DN.Diagram); results.addAll(diagrams); } return results; } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 hasDiameterValue(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return directPropertyValueFunction(DistrictNetworkResource.getInstance(graph).Edge_HasDiameter, 0); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 hasNominalMassFlowValue(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return directPropertyValueFunction(DistrictNetworkResource.getInstance(graph).Edge_HasNominalMassFlow, 0); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 hasNominalSupplyPressure(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return directPropertyValueFunction(DistrictNetworkResource.getInstance(graph).Vertex_HasSupplyPressure, 0); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 hasElevation(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return directPropertyValueFunction(DistrictNetworkResource.getInstance(graph).Vertex_HasElevation, 0); } private static final Function1 ONE = new FunctionImpl1() { private final Double ONE = 1.0; @Override public Double apply(Resource edge) { return ONE; } @Override public String toString() { return "1"; } }; @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Function1 constantOne(ReadGraph graph, Resource resource, Variable context) throws DatabaseException { return ONE; } private static Function1 directPropertyValueFunction(Resource property, double defaultValue) throws DatabaseException { Double def = defaultValue; return new FunctionImpl1() { @Override public Double apply(Resource edge) { ReadGraph graph = (ReadGraph) SCLContext.getCurrent().get("graph"); try { Double d = graph.getPossibleRelatedValue(edge, property, Bindings.DOUBLE); return d != null ? d : def; } catch (DatabaseException e) { LOGGER.error("Failed to evaluate property value", e); return def; } } }; } private static class RangeValidator implements Function1 { private double min; private double max; public RangeValidator(double min, double max) { this.min = min; this.max = max; } @Override public String apply(String s) { try { double d = Double.parseDouble(s); if (d < min) return "Value must be greater than or equal to " + min; if (d > max) return "Value must be less than or equal to " + max; return null; } catch (NumberFormatException e) { return "Specified value is not a number"; } } } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object hueValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { return new RangeValidator(0, 360); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object saturationValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { return new RangeValidator(0, 100); } @SCLValue(type = "ReadGraph -> Resource -> Variable -> b") public static Object brightnessValidator(ReadGraph graph, Resource r, Variable context) throws DatabaseException { String importEntry = null; Resource root = Variables.getPossibleIndexRoot(graph, context); if (root != null) { Resource sclmain = Layer0Utils.getPossibleChild(graph, root, "SCLMain"); if (sclmain != null) { importEntry = graph.getPossibleURI(sclmain); } } SCLContext ctx = SCLContext.getCurrent(); Object oldGraph = ctx.put("graph", graph); try { return new BrightnessExpressionValidator( importEntry != null ? Arrays.asList(importEntry) : Collections.emptyList()); } finally { ctx.put("graph", oldGraph); } } private static class BrightnessExpressionValidator implements Function1 { private CommandSession session; public BrightnessExpressionValidator(List importEntries) { this.session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, SCLReportingHandler.DEFAULT); this.session.setImportEntries(imports(importEntries)); } private ArrayList imports(List entries) { ArrayList result = new ArrayList<>(); entries.stream().map(CommandSessionImportEntry::new).forEach(result::add); if (entries.isEmpty()) result.add(new CommandSessionImportEntry("Simantics/District/SCLMain")); return result; } @Override public String apply(String s) { s = s.trim(); if (!s.startsWith("=")) return "Expression expected, must start with '='"; CompilationError[] errors = session.validate(s.substring(1)); if(errors.length == 0) return null; return errors[0].description; } } }