package org.simantics.simulation.export; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.osgi.service.prefs.Preferences; import org.simantics.NameLabelUtil; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.BooleanAccessor; import org.simantics.databoard.accessor.RecordAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.type.Component; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.DoubleType; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.util.URIUtil; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.PossibleObjectWithType; import org.simantics.db.exception.DatabaseException; import org.simantics.db.request.Read; import org.simantics.export.core.ExportContext; import org.simantics.export.core.error.ExportException; import org.simantics.export.core.intf.ExportClass; import org.simantics.export.core.util.ExportQueries; import org.simantics.layer0.Layer0; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.utils.strings.AlphanumComparator; /** * Common mechanism for CSV and Chart exports. * * @author toni.kalajainen@semantum.fi */ public abstract class ExperimentExportClass implements ExportClass { public static ChildReference P_EXPERIMENT = new LabelReference("Experiment"); public static ChildReference P_EXPERIMENT_START = ChildReference.parsePath("Experiment/Start Time"); public static ChildReference P_EXPERIMENT_END = ChildReference.parsePath("Experiment/End Time"); public RecordType options(ExportContext context, Collection content) throws ExportException { RecordType options; RecordType experimentOptions; Datatype second = new DoubleType("s"); experimentOptions = new RecordType(); experimentOptions.addComponent("Start Time", second); experimentOptions.addComponent("End Time", second); try { List models = context.session.syncRequest( ExportQueries.toModels(content) ); for (Resource model : models) { List runs = context.session.syncRequest( getExperimentRuns(model) ); String modelLabel = context.session.syncRequest( ExportQueries.label( model ) ); if ( modelLabel==null ) continue; List runLabels = new ArrayList(); for ( Resource run : runs ) { String runLabel = context.session.syncRequest( getRunLabel(run) ); if ( runLabel == null ) continue; runLabels.add(runLabel); } Collections.sort( runLabels, AlphanumComparator.CASE_INSENSITIVE_COMPARATOR ); RecordType modelRunSelection = new RecordType(); modelRunSelection.metadata.put("style", "dialog"); for ( String runLabel : runLabels ) { modelRunSelection.addComponent(runLabel, Datatypes.BOOLEAN); } experimentOptions.addComponent(modelLabel+", experiment runs", modelRunSelection); } } catch (DatabaseException e) { throw new ExportException( e ); } options = new RecordType(); options.addComponent("Experiment", experimentOptions); return options; } public void fillDefaultPrefs(final ExportContext ctx, final Variant options) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); ra.setValue(P_EXPERIMENT_END, Bindings.DOUBLE, 86400.0); } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } // Select the most latest experiments try { Accessor ra = Accessors.getAccessor( options ); ra = ra.getComponent( P_EXPERIMENT ); for (ModelRef modelRef : getResult(ctx, options, false)) { for (ExperimentRef experimentRef : modelRef.experiments) { for (RunRef runRef : experimentRef.runs) { if ( runRef.isActive ) { try { BooleanAccessor ba = ra.getComponent(runRef.optionsRef); ba.setValue(true); } catch(AccessorConstructionException ae) {} } } } } } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } catch (DatabaseException e1) { throw new ExportException( e1 ); } } public void savePref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); Double startTime = (Double) ra.getValue(P_EXPERIMENT_START, Bindings.DOUBLE ); if ( startTime != null ) contentScopeNode.putDouble(P_EXPERIMENT_START.tail().toString(), startTime); Double endTime = (Double) ra.getValue(P_EXPERIMENT_END, Bindings.DOUBLE ); if ( endTime != null ) contentScopeNode.putDouble(P_EXPERIMENT_END.tail().toString(), endTime); } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } } public void loadPref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { try { RecordAccessor ra = Accessors.getAccessor(options); double startTime = contentScopeNode.getDouble(P_EXPERIMENT_START.tail().toString(), -Double.MAX_VALUE); if ( startTime != -Double.MAX_VALUE ) ra.setValue(P_EXPERIMENT_START, Bindings.DOUBLE, startTime ); double endTime = contentScopeNode.getDouble(P_EXPERIMENT_END.tail().toString(), -Double.MAX_VALUE); if ( endTime != -Double.MAX_VALUE ) ra.setValue(P_EXPERIMENT_END, Bindings.DOUBLE, endTime ); } catch (AccessorConstructionException e) { throw new ExportException(e); } catch (AccessorException e) { throw new ExportException(e); } } /** * Get a request that returns experiments runs in format of * "Experiment\Experiment Runs". * * @param model * @return */ public static Read> getExperimentRuns(final Resource model) { return new Read>() { @Override public List perform(ReadGraph graph) throws DatabaseException { List result = new ArrayList(); Layer0 b = Layer0.getInstance(graph); SimulationResource SIMU = SimulationResource.getInstance(graph); for (Resource config : graph.getObjects(model, b.ConsistsOf)) { if (graph.isInstanceOf(config, SIMU.Experiment)) { for (Resource run : graph.getObjects(config, b.ConsistsOf)) { if (graph.isInstanceOf(run, SIMU.Run)) { result.add( run ); } } } } return result; } }; } /** * Returns a label in format of "Experiment\Run" * * @param run * @return the label or null */ public static Read getRunLabel(final Resource run) { return new Read() { @Override public String perform(ReadGraph graph) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); SimulationResource SIMU = SimulationResource.getInstance(graph); Resource experiment = graph.syncRequest( new PossibleObjectWithType(run, L0.PartOf, SIMU.Experiment) ); if ( experiment == null ) return null; String experimentLabel = NameLabelUtil.modalName(graph, experiment); String runLabel = NameLabelUtil.modalName(graph, run); if ( experimentLabel == null || runLabel == null ) return null; return experimentLabel+"\\"+runLabel; } }; } /** * Get Run resource using a label "Experiment\Run" * * @param model * @param runLabel * @return resource */ public static Read getRunByLabel(final Resource model, final String runLabel) { return new Read() { @Override public Resource perform(ReadGraph graph) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); SimulationResource SIMU = SimulationResource.getInstance(graph); for (Resource config : graph.getObjects(model, L0.ConsistsOf)) { if (graph.isInstanceOf(config, SIMU.Experiment)) { String experimentLabel = NameLabelUtil.modalName(graph, config); if ( experimentLabel == null ) continue; if ( !runLabel.startsWith(experimentLabel) ) continue; for (Resource run : graph.getObjects(config, L0.ConsistsOf)) { if (graph.isInstanceOf(run, SIMU.Run)) { String lbl2 = NameLabelUtil.modalName(graph, run); if ( lbl2 == null ) continue; if ( runLabel.equals( experimentLabel+"\\"+lbl2 ) ) return run; } } } } return null; } }; } public static Read> getChildByLabelAndType(final Resource subject, final Resource type, final String label) { return new Read>() { @Override public List perform(ReadGraph graph) throws DatabaseException { List result = new ArrayList(); Layer0 L0 = Layer0.getInstance(graph); for ( Resource child : graph.getObjects(subject, L0.ConsistsOf )) { if ( !graph.isInstanceOf(child, type) ) continue; String lbl = NameLabelUtil.modalName(graph, child); if ( lbl==null ) continue; if ( lbl.equals(label)) result.add( child ); } return result; } }; } /** * Read Model/Experiment/Run from options * * @param ctx * @param optionsBinding * @param options * @param returnOnlyEnabledInOptions * @return * @throws DatabaseException */ public static List getResult(final ExportContext ctx, final Variant options, final boolean returnOnlyEnabledInOptions) throws DatabaseException { return ctx.session.syncRequest( new Read>() { @Override public List perform(ReadGraph graph) throws DatabaseException { try { List result = new ArrayList(); Layer0 L0 = Layer0.getInstance(graph); SimulationResource SIMU = SimulationResource.getInstance(graph); Resource project = graph.getResource( ctx.project ); Accessor ra = Accessors.getAccessor(options); ra = ra.getComponent(P_EXPERIMENT); RecordType type = (RecordType)((RecordType) options.type()).getComponentType("Experiment"); if ( type != null ) { for ( Component c : type.getComponents() ) { int endIndex = c.name.length() - ", experiment runs".length(); if ( endIndex <= 0 ) continue; String modelName = c.name.substring(0, endIndex); if ( modelName.isEmpty() ) continue; List models = graph.syncRequest( getChildByLabelAndType(project, SIMU.Model, modelName) ); for (Resource model : models ) { ModelRef modelRef = new ModelRef(); modelRef.resource = model; modelRef.label = modelName; modelRef.uri = graph.getURI(model); if ( c.type instanceof RecordType == false ) continue; RecordType rt = (RecordType) c.type; for (Component cc : rt.getComponents()) { String pp = cc.name; String[] parts = pp.split("\\\\"); if ( parts.length!=2 ) continue; String experimentLabel = parts[0]; String runLabel = parts[1]; for ( Resource experiment : graph.syncRequest( getChildByLabelAndType(model, SIMU.Experiment, experimentLabel) ) ) { ExperimentRef experimentRef = new ExperimentRef(); experimentRef.resource = experiment; experimentRef.label = experimentLabel; experimentRef.uri = graph.getURI(experiment); for ( Resource run : graph.syncRequest( getChildByLabelAndType(experiment, SIMU.Run, runLabel) ) ) { RunRef runRef = new RunRef(); runRef.optionsRef = new LabelReference(c.name, new LabelReference(experimentLabel+"\\"+runLabel));; try { BooleanAccessor ba = ra.getComponent( runRef.optionsRef ); runRef.isEnabled = ba.getValue(); if ( !runRef.isEnabled && returnOnlyEnabledInOptions ) continue; } catch (AccessorException e) { if ( returnOnlyEnabledInOptions ) continue; } runRef.resource = run; runRef.label = runLabel; runRef.uri = graph.getURI(run); runRef.isActive = graph.hasStatement(run, SIMU.IsActive); runRef.identifier = graph.getRelatedValue(run, L0.HasName, Bindings.STRING); runRef.historyFolder = getExperimentDirectory(modelRef.resource, experimentRef.resource, "result-" + runRef.identifier); experimentRef.runs.add(runRef); } if ( !experimentRef.runs.isEmpty() ) modelRef.experiments.add(experimentRef); } } if ( !modelRef.experiments.isEmpty() ) result.add( modelRef ); } } } return result; } catch (AccessorConstructionException e) { throw new DatabaseException( e ); } }}); } public static ModelRef getModelRefByResource(List modelRefs, Resource model) { for (ModelRef modelRef : modelRefs) if ( modelRef.resource.equals(model) ) return modelRef; return null; } public static File getExperimentDirectory(Resource model, Resource experiment, String... subdirs) throws DatabaseException { String[] dirs = new String[4 + subdirs.length]; dirs[0] = "resources"; dirs[1] = "model-" + model.getResourceId(); dirs[2] = "experiments"; dirs[3] = "" + experiment.getResourceId(); System.arraycopy(subdirs, 0, dirs, 4, subdirs.length); return getWorkspacePath(false, dirs); } /** * @param escapeNames true to run each path segment through * {@link URIUtil#encodeFilename(String)} * @param relativeSegments path segments to append to the workspace root * path * @return the designated path within the workspace */ public static File getWorkspacePath(boolean escapeNames, String... relativeSegments) { IPath finalPath = Platform.getLocation(); for (int i = 0; i < relativeSegments.length; ++i) finalPath = finalPath.append(escapeNames ? URIUtil.encodeFilename(relativeSegments[i]) : relativeSegments[i]); return finalPath.toFile(); } /** * @return the workspace root path as a File */ public static File getWorkspacePath() { return getWorkspacePath(false); } public static class ModelRef { public String uri; public Resource resource; public String label; public List experiments = new ArrayList(); public int runCount() { int count = 0; for ( ExperimentRef er : experiments ) count += er.runs.size(); return count; } public int enabledRunCount() { int count = 0; for ( ExperimentRef er : experiments ) count += er.enabledRunCount(); return count; } public List getRunRefs() { List result = new ArrayList(); for ( ExperimentRef er : experiments ) result.addAll( er.runs ); return result; } } public static class ExperimentRef { public String uri; public Resource resource; public String label; public List runs = new ArrayList(); public int enabledRunCount() { int count = 0; for ( RunRef rr : runs ) if ( rr.isEnabled ) count++; return count; } } public static class RunRef { public String uri; public Resource resource; public String label; public ChildReference optionsRef; public boolean isActive; public boolean isEnabled; public File historyFolder; public String identifier; } }