package org.simantics.charts.export; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.UUID; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.InstanceScope; import org.osgi.service.prefs.Preferences; import org.simantics.Simantics; import org.simantics.charts.preference.ChartPreferences; import org.simantics.charts.query.MilestoneSpecQuery; import org.simantics.charts.query.TrendSpecQuery; import org.simantics.databoard.Accessors; import org.simantics.databoard.accessor.RecordAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.type.RecordType; import org.simantics.databoard.type.UnionType; import org.simantics.db.Resource; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PossibleModel; import org.simantics.export.core.ExportContext; import org.simantics.export.core.error.ExportException; import org.simantics.export.core.manager.Content; import org.simantics.export.core.pdf.ExportPdfWriter; import org.simantics.export.core.pdf.ExportPdfWriter.Page; import org.simantics.export.core.util.ExportQueries; import org.simantics.export.core.util.ExporterUtils; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.history.History; import org.simantics.history.HistoryException; import org.simantics.history.HistoryManager; import org.simantics.simulation.experiment.IExperiment; import org.simantics.simulation.experiment.IHistoryExperiment; import org.simantics.simulation.export.ExperimentExportClass; import org.simantics.simulation.project.ExperimentManager; import org.simantics.simulation.project.IExperimentManager; import org.simantics.trend.TrendInitializer; import org.simantics.trend.configuration.ItemPlacement; import org.simantics.trend.configuration.TimeFormat; import org.simantics.trend.configuration.TrendSpec; import org.simantics.trend.impl.MilestoneSpec; import org.simantics.trend.impl.TrendNode; import org.simantics.utils.datastructures.MapList; import org.simantics.utils.format.ValueFormat; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.threads.WorkerThread; public class ExportChartPDF extends ExperimentExportClass { public static final RecordType chartOptions; public static String S_CHART = "Chart"; public static ChildReference P_ITEMPLACEMENT = ChildReference.parsePath(S_CHART+"/Item Placement"); public static ChildReference P_TIMEFORMAT = ChildReference.parsePath(S_CHART+"/Time Format"); public static ChildReference P_VALUEFORMAT = ChildReference.parsePath(S_CHART+"/Value Format"); static { chartOptions = new RecordType(); chartOptions.addComponent("Item Placement", UnionType.newEnum("Stacked", "Overlapping")); chartOptions.addComponent("Time Format", UnionType.newEnum("Decimal", "Time")); chartOptions.addComponent("Value Format", UnionType.newEnum("Currency", "Scientific", "Engineering", "Default")); } @Override public void export(List contents, Object handle, ExportContext ctx, Variant options, IProgressMonitor monitor, MapList attachmentMap ) throws ExportException { // Flush all experiments, just in case. IExperimentManager em = Simantics.getProject().getHint( ExperimentManager.KEY_EXPERIMENT_MANAGER ); if ( em != null ) { for (IExperiment exp : em.getExperiments()) { if ( exp instanceof IHistoryExperiment ) { IHistoryExperiment he = (IHistoryExperiment) exp; try { he.flushHistory(); } catch (HistoryException e) { } } } } final ExportPdfWriter writer = (ExportPdfWriter) handle; final WorkerThread workerThread = new WorkerThread("Chart PDF Painter"); workerThread.start(); try { final RecordAccessor ra = Accessors.getAccessor( options ); // Get a list of the history managers the user has selected. List modelRefs = ExperimentExportClass.getResult(ctx, options, true); List charts = new ArrayList(); for ( Content content : contents ) { if (monitor.isCanceled()) throw new OperationCanceledException(); Resource chartRes = ctx.session.syncRequest( ExportQueries.toResource(content.url) ); Resource model = ctx.session.syncRequest( new PossibleModel( chartRes ) ); for (ModelRef modelRef : modelRefs) { if ( !modelRef.resource.equals(model) ) continue; for (ExperimentRef experimentRef : modelRef.experiments) { for (RunRef runRef : experimentRef.runs) { if ( runRef.historyFolder == null || !runRef.historyFolder.exists() ) continue; HistoryManager history = History.openFileHistory( runRef.historyFolder ); ChartSettings cs = new ChartSettings(); cs.history = history; cs.modelRef = modelRef; cs.experimentRef = experimentRef; cs.runRef = runRef; cs.chartRes = chartRes; charts.add( cs ); } } } } for ( final ChartSettings cs : charts ) { UUID id = UUID.randomUUID(); final TrendSpec trendSpec = ctx.session.syncRequest( new TrendSpecQuery( id, cs.chartRes ) ); if ( cs.modelRef.enabledRunCount() > 1 ) { // Add run ref to the label trendSpec.name += " / " + cs.runRef.label; } final MilestoneSpec milestones = ctx.session.syncRequest( new MilestoneSpecQuery( cs.experimentRef.resource ) ); //final CanvasContext cctx = new CanvasContext( workerThread ); final ExportException[] error = new ExportException[1]; ThreadUtils.syncExec(workerThread, new Runnable() { @Override public void run() { CanvasContext cctx = TrendInitializer.createDefaultCanvas(workerThread, cs.history, null, null, trendSpec); Page pdfPage = null; try { TrendNode trend = TrendInitializer.getTrendNode( cctx ); trend.printing = true; String s = ExporterUtils.getUnionValue(ra, P_ITEMPLACEMENT); if ( s!=null ) trend.itemPlacement = ItemPlacement.valueOf(s); s = ExporterUtils.getUnionValue(ra, P_TIMEFORMAT); if ( s!=null ) trend.timeFormat = TimeFormat.valueOf(s); s = ExporterUtils.getUnionValue(ra, P_VALUEFORMAT); if ( s!=null ) trend.valueFormat = ValueFormat.valueOf(s); if (milestones!=null) trend.setMilestones(milestones); pdfPage = writer.createPage( null ); Graphics2D g2d = pdfPage.createGraphics( true ); try { Rectangle2D clip = new Rectangle2D.Double(0, 0, pdfPage.getWidth(), pdfPage.getHeight()); g2d.setClip( clip ); g2d.scale(0.25, 0.25); trend.autoscale(true, true); trend.zoomOut(); trend.layout(); trend.render( g2d ); } finally { trend.cleanup(); g2d.dispose(); cs.history.close(); } } catch (ExportException e) { error[0] = e; } finally { cctx.dispose(); if ( pdfPage != null ) try { pdfPage.close(); } catch (ExportException e) { if ( error[0]==null ) error[0] = e; } } } }); if ( error[0] != null ) throw error[0]; } } catch (DatabaseException e) { throw new ExportException( e ); } catch (AccessorConstructionException e) { throw new ExportException( e ); } finally { workerThread.stopDispatchingEvents(true); } } @Override public RecordType options(ExportContext context, Collection content) throws ExportException { RecordType options = super.options(context, content); options.addComponent(S_CHART, chartOptions); return options; } @Override public void fillDefaultPrefs(ExportContext ctx, Variant options) throws ExportException { super.fillDefaultPrefs(ctx, options); // Read from eclipse preferences try { RecordAccessor ra = Accessors.getAccessor(options); Preferences instPrefs = InstanceScope.INSTANCE.getNode( ChartPreferences.P_NODE ); Preferences defaultPrefs = DefaultScope.INSTANCE.getNode( ChartPreferences.P_NODE ); String s; s = ExporterUtils.getPrefString(instPrefs, defaultPrefs, ChartPreferences.P_ITEMPLACEMENT); ExporterUtils.setUnionValue(ra, P_ITEMPLACEMENT, s); s = ExporterUtils.getPrefString(instPrefs, defaultPrefs, ChartPreferences.P_TIMEFORMAT); ExporterUtils.setUnionValue(ra, P_TIMEFORMAT, s); s = ExporterUtils.getPrefString(instPrefs, defaultPrefs, ChartPreferences.P_VALUEFORMAT); ExporterUtils.setUnionValue(ra, P_VALUEFORMAT, s); } catch (AccessorConstructionException e) { throw new ExportException(e); } } @Override public void savePref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { super.savePref(options, contentScopeNode, workbenchScopeNode); try { RecordAccessor ra = Accessors.getAccessor(options); String s; s = ExporterUtils.getUnionValue(ra, P_ITEMPLACEMENT); ExporterUtils.setPrefString(contentScopeNode, ChartPreferences.P_ITEMPLACEMENT, s); ExporterUtils.setPrefString(workbenchScopeNode, ChartPreferences.P_ITEMPLACEMENT, s); s = ExporterUtils.getUnionValue(ra, P_TIMEFORMAT); ExporterUtils.setPrefString(contentScopeNode, ChartPreferences.P_TIMEFORMAT, s); ExporterUtils.setPrefString(workbenchScopeNode, ChartPreferences.P_TIMEFORMAT, s); s = ExporterUtils.getUnionValue(ra, P_VALUEFORMAT); ExporterUtils.setPrefString(contentScopeNode, ChartPreferences.P_VALUEFORMAT, s); ExporterUtils.setPrefString(workbenchScopeNode, ChartPreferences.P_VALUEFORMAT, s); } catch (AccessorConstructionException e) { throw new ExportException(e); } } @Override public void loadPref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException { super.loadPref(options, contentScopeNode, workbenchScopeNode); try { RecordAccessor ra = Accessors.getAccessor(options); String s; s = ExporterUtils.getPrefString(contentScopeNode, ChartPreferences.P_ITEMPLACEMENT); if ( s==null ) s = ExporterUtils.getPrefString(workbenchScopeNode, ChartPreferences.P_ITEMPLACEMENT); ExporterUtils.setUnionValue(ra, P_ITEMPLACEMENT, s); s = ExporterUtils.getPrefString(contentScopeNode, ChartPreferences.P_TIMEFORMAT); if ( s==null ) s = ExporterUtils.getPrefString(workbenchScopeNode, ChartPreferences.P_TIMEFORMAT); ExporterUtils.setUnionValue(ra, P_TIMEFORMAT, s); s = ExporterUtils.getPrefString(contentScopeNode, ChartPreferences.P_VALUEFORMAT); if ( s==null ) s = ExporterUtils.getPrefString(workbenchScopeNode, ChartPreferences.P_VALUEFORMAT); ExporterUtils.setUnionValue(ra, P_VALUEFORMAT, s); } catch (AccessorConstructionException e) { throw new ExportException(e); } } @Override public List validate(String contentUri, ExportContext context, Variant options) { return Collections.emptyList(); } static class ChartSettings { Resource chartRes; ModelRef modelRef; ExperimentRef experimentRef; RunRef runRef; HistoryManager history; } }