d4d48060187b27212d4eb14c7bdd85b9f29a057d
[simantics/platform.git] / bundles / org.simantics.charts / src / org / simantics / charts / ui / CSVExporter.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.charts.ui;
13
14 import java.io.BufferedOutputStream;
15 import java.io.File;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.PrintStream;
19 import java.io.RandomAccessFile;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26
27 import org.eclipse.core.commands.ExecutionException;
28 import org.eclipse.core.runtime.IProgressMonitor;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Status;
31 import org.eclipse.core.runtime.SubMonitor;
32 import org.eclipse.core.runtime.preferences.IScopeContext;
33 import org.eclipse.core.runtime.preferences.InstanceScope;
34 import org.eclipse.jface.operation.IRunnableWithProgress;
35 import org.osgi.service.prefs.BackingStoreException;
36 import org.osgi.service.prefs.Preferences;
37 import org.simantics.Simantics;
38 import org.simantics.charts.Activator;
39 import org.simantics.charts.editor.ChartData;
40 import org.simantics.charts.editor.ChartKeys;
41 import org.simantics.databoard.binding.error.BindingException;
42 import org.simantics.databoard.serialization.SerializationException;
43 import org.simantics.db.ReadGraph;
44 import org.simantics.db.Resource;
45 import org.simantics.db.Session;
46 import org.simantics.db.common.NamedResource;
47 import org.simantics.db.common.request.UniqueRead;
48 import org.simantics.db.common.utils.Logger;
49 import org.simantics.db.exception.DatabaseException;
50 import org.simantics.db.layer0.request.PossibleModel;
51 import org.simantics.history.HistoryException;
52 import org.simantics.history.csv.CSVFormatter;
53 import org.simantics.modeling.preferences.CSVPreferences;
54 import org.simantics.utils.datastructures.hints.IHintContext.Key;
55 import org.simantics.utils.format.FormattingUtils;
56 import org.simantics.utils.ui.dialogs.ShowMessage;
57
58 /**
59  * @author Antti Villberg
60  */
61 public class CSVExporter implements IRunnableWithProgress {
62
63     CSVExportPlan exportModel;
64
65     public CSVExporter(CSVExportPlan exportModel) {
66         this.exportModel = exportModel;
67     }
68
69     @Override
70     public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
71         SubMonitor progress = SubMonitor.convert(monitor, 50);
72         try {
73             exportModel(progress.newChild(50, SubMonitor.SUPPRESS_NONE));
74         } catch (IOException e) {
75             throw new InvocationTargetException(e);
76         } catch (DatabaseException e) {
77             throw new InvocationTargetException(e);
78         } catch (BindingException e) {
79             throw new InvocationTargetException(e);
80         } finally {
81             monitor.done();
82         }
83     }
84
85     void exportModel(SubMonitor mon) throws IOException, DatabaseException, SerializationException, BindingException{
86         
87         try {
88             doExport(mon, exportModel.exportLocation, exportModel);
89         } catch (ExecutionException e) {
90             e.printStackTrace();
91             Logger.defaultLogError(e);
92             mon.setCanceled(true);
93             ShowMessage.showError("Export failed.", "Internal application error in export. See log for details.");
94         } finally {
95             mon.setWorkRemaining(0);
96         }
97         
98     }
99     
100     private static Set<Resource> resolveContainingModels(final Collection<NamedResource> res) throws DatabaseException {
101         return Simantics.getSession().syncRequest(new UniqueRead<Set<Resource>>() {
102             @Override
103             public Set<Resource> perform(ReadGraph graph) throws DatabaseException {
104                 Set<Resource> models = new HashSet<Resource>();
105                 for (NamedResource r : res) {
106                     Resource m = graph.syncRequest(new PossibleModel(r.getResource()));
107                     if (m != null)
108                         models.add(m);
109                 }
110                 return models;
111             }
112         });
113     }
114     
115     public static void doExport(IProgressMonitor monitor, final File f, final CSVExportPlan plan) throws ExecutionException, IOException {
116         
117         IScopeContext context = InstanceScope.INSTANCE;
118         Preferences node = context.getNode(CSVPreferences.P_NODE);
119
120         node.putDouble(CSVPreferences.P_CSV_START_TIME, plan.startTime);
121         node.putDouble(CSVPreferences.P_CSV_TIME_STEP, plan.timeStep);
122         node.put(CSVPreferences.P_CSV_DECIMAL_SEPARATOR, plan.decimalSeparator.toPreference());
123         node.put(CSVPreferences.P_CSV_COLUMN_SEPARATOR, plan.columnSeparator.toPreference());
124         
125         node.putBoolean(CSVPreferences.P_CSV_RESAMPLE, plan.resample);
126         node.put(CSVPreferences.P_CSV_SAMPLING_MODE, plan.samplingMode.toPreference());
127         
128         node.putInt(CSVPreferences.P_CSV_TIME_DIGITS, plan.timeDigits);
129         node.putInt(CSVPreferences.P_CSV_FLOAT_DIGITS, plan.floatDigits);
130         node.putInt(CSVPreferences.P_CSV_DOUBLE_DIGITS, plan.doubleDigits);
131
132         try {
133             node.flush();
134         } catch (BackingStoreException ex) {
135             Activator.getDefault().getLog().log(
136                     new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Could not store preferences for node " + node.absolutePath()));
137         }
138
139         Set<Resource> models;
140         try {
141             models = resolveContainingModels(plan.models);
142         } catch (DatabaseException e3) {
143             throw new ExecutionException("Containing model resolution failed.", e3);
144         }
145         if (models.isEmpty())
146             throw new ExecutionException("Selected resources are not part of any model");
147         if (models.size() > 1)
148             throw new ExecutionException("Selected resources are part of several models, only subscriptions from a single model can be selected");
149         Resource model = models.iterator().next();
150         Key chartDataKey = ChartKeys.chartSourceKey(model);
151         
152         final ChartData data = Simantics.getProject().getHint(chartDataKey);
153         if ( data == null ) {
154             throw new ExecutionException("There is no "+chartDataKey);
155         }
156         if ( data.history == null ) {
157             throw new ExecutionException("There is no history in "+chartDataKey);
158         }
159
160         final CSVFormatter csv = new CSVFormatter();
161
162         csv.setStartTime( plan.startTime );
163         csv.setTimeStep( plan.timeStep );
164         csv.setDecimalSeparator( plan.decimalSeparator );
165         csv.setColumnSeparator( plan.columnSeparator  );
166         csv.setResample( plan.resample );
167         csv.setNumberInterpolation( plan.samplingMode );
168         csv.setTimeFormat( FormattingUtils.significantDigitFormat( plan.timeDigits ) );
169         csv.setFloatFormat( FormattingUtils.significantDigitFormat( plan.floatDigits ) );
170         csv.setNumberFormat( FormattingUtils.significantDigitFormat( plan.doubleDigits ) );
171
172                 try {
173                         Session session = Simantics.getSession();
174                         List<Resource> list = new ArrayList<Resource>();
175                         for(NamedResource nr : plan.models) list.add(nr.getResource());
176                         session.sync( new CSVParamsQuery(data.history, csv, list) );
177                         csv.sort();
178                 } catch (DatabaseException e2) {
179                         throw new ExecutionException(e2.getMessage(), e2);
180                 } catch (HistoryException e) {
181                         throw new ExecutionException(e.getMessage(), e);
182                 }
183         
184                  try {
185              // Ensure all views are built.
186              monitor.beginTask("Exporting Time Series as CSV...", IProgressMonitor.UNKNOWN);
187              try {
188                  data.collector.flush();
189                 if ( !f.exists() ) {
190                         f.createNewFile();
191                 } else {
192                         RandomAccessFile raf = new RandomAccessFile(f, "rw");
193                         raf.setLength(0);
194                         raf.close();
195                 }
196                 
197                 FileOutputStream fos = new FileOutputStream(f, true);
198                 BufferedOutputStream bos = new BufferedOutputStream( fos );
199                 try {
200                         PrintStream ps = new PrintStream( bos );
201                         csv.formulate2( new CSVProgressMonitor( monitor ), ps );
202                         bos.flush();
203                                         } finally {
204                         fos.close();
205                 }
206                 } catch (HistoryException e) {
207                 throw new ExecutionException(e.getMessage(), e);
208              } catch (IOException e1) {
209                 throw new ExecutionException(e1.getMessage(), e1);
210              }
211              monitor.setTaskName("Done");
212          } finally {
213              monitor.done();
214          }
215         
216     }
217     
218 }