/******************************************************************************* * Copyright (c) 2012,2017 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.charts.ui; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.operation.IRunnableWithProgress; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.simantics.Simantics; import org.simantics.charts.Activator; import org.simantics.charts.editor.ChartData; import org.simantics.charts.editor.ChartKeys; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.serialization.SerializationException; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PossibleModel; import org.simantics.history.HistoryException; import org.simantics.history.csv.CSVFormatter; import org.simantics.modeling.preferences.CSVPreferences; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.format.FormattingUtils; import org.simantics.utils.ui.dialogs.ShowMessage; /** * @author Antti Villberg */ public class CSVExporter implements IRunnableWithProgress { CSVExportPlan exportModel; public CSVExporter(CSVExportPlan exportModel) { this.exportModel = exportModel; } @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { SubMonitor progress = SubMonitor.convert(monitor, 50); try { exportModel(progress.newChild(50, SubMonitor.SUPPRESS_NONE)); } catch (IOException e) { throw new InvocationTargetException(e); } catch (DatabaseException e) { throw new InvocationTargetException(e); } catch (BindingException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } void exportModel(SubMonitor mon) throws IOException, DatabaseException, SerializationException, BindingException{ try { doExport(mon, exportModel.exportLocation, exportModel); } catch (ExecutionException e) { e.printStackTrace(); Logger.defaultLogError(e); mon.setCanceled(true); ShowMessage.showError("Export failed.", "Internal application error in export. See log for details."); } finally { mon.setWorkRemaining(0); } } private static Set resolveContainingModels(Collection res) throws DatabaseException { return Simantics.getSession().syncRequest(new UniqueRead>() { @Override public Set perform(ReadGraph graph) throws DatabaseException { Set models = new HashSet<>(); for (Resource r : res) { Resource m = graph.syncRequest(new PossibleModel(r)); if (m != null) models.add(m); } return models; } }); } public static void doExport(IProgressMonitor monitor, final File f, final CSVExportPlan plan) throws ExecutionException, IOException { IScopeContext context = InstanceScope.INSTANCE; Preferences node = context.getNode(CSVPreferences.P_NODE); node.putDouble(CSVPreferences.P_CSV_START_TIME, plan.startTime); node.putDouble(CSVPreferences.P_CSV_TIME_STEP, plan.timeStep); node.put(CSVPreferences.P_CSV_DECIMAL_SEPARATOR, plan.decimalSeparator.toPreference()); node.put(CSVPreferences.P_CSV_COLUMN_SEPARATOR, plan.columnSeparator.toPreference()); node.putBoolean(CSVPreferences.P_CSV_RESAMPLE, plan.resample); node.put(CSVPreferences.P_CSV_SAMPLING_MODE, plan.samplingMode.toPreference()); node.putInt(CSVPreferences.P_CSV_TIME_DIGITS, plan.timeDigits); node.putInt(CSVPreferences.P_CSV_FLOAT_DIGITS, plan.floatDigits); node.putInt(CSVPreferences.P_CSV_DOUBLE_DIGITS, plan.doubleDigits); try { node.flush(); } catch (BackingStoreException ex) { Activator.getDefault().getLog().log( new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Could not store preferences for node " + node.absolutePath())); } Set models; try { models = resolveContainingModels(plan.items); } catch (DatabaseException e3) { throw new ExecutionException("Containing model resolution failed.", e3); } if (models.isEmpty()) throw new ExecutionException("Selected resources are not part of any model"); if (models.size() > 1) throw new ExecutionException("Selected resources are part of several models, only subscriptions from a single model can be selected"); Resource model = models.iterator().next(); Key chartDataKey = ChartKeys.chartSourceKey(model); final ChartData data = Simantics.getProject().getHint(chartDataKey); if ( data == null ) { throw new ExecutionException("There is no "+chartDataKey); } if ( data.history == null ) { throw new ExecutionException("There is no history in "+chartDataKey); } final CSVFormatter csv = new CSVFormatter(); csv.setStartTime( plan.startTime ); csv.setTimeStep( plan.timeStep ); csv.setDecimalSeparator( plan.decimalSeparator ); csv.setColumnSeparator( plan.columnSeparator ); csv.setResample( plan.resample ); csv.setNumberInterpolation( plan.samplingMode ); csv.setTimeFormat( FormattingUtils.significantDigitFormat( plan.timeDigits ) ); csv.setFloatFormat( FormattingUtils.significantDigitFormat( plan.floatDigits ) ); csv.setNumberFormat( FormattingUtils.significantDigitFormat( plan.doubleDigits ) ); try { Simantics.getSession().syncRequest( new CSVParamsQuery(data.history, csv, new ArrayList<>(plan.items)) ); csv.sort(); // Ensure all views are built. monitor.beginTask("Exporting Time Series as CSV...", IProgressMonitor.UNKNOWN); data.collector.flush(); // Truncate existing file it if happens to exist. try (RandomAccessFile raf = new RandomAccessFile(f, "rw")) { raf.setLength(0); } // Write CSV try (PrintStream ps = new PrintStream( new BufferedOutputStream( new FileOutputStream(f, true) ))) { csv.formulate2( new CSVProgressMonitor( monitor ), ps ); ps.flush(); } monitor.setTaskName("Done"); } catch (DatabaseException e2) { throw new ExecutionException(e2.getMessage(), e2); } catch (HistoryException e) { throw new ExecutionException(e.getMessage(), e); } finally { monitor.done(); } } }