[#2525] Allow component analysis to be exported to a CSV file

This commit is contained in:
SiboVG 2024-08-26 02:26:46 +02:00
parent 1840bd4c0e
commit 525190b37f
8 changed files with 397 additions and 101 deletions

View File

@ -7,15 +7,23 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import info.openrocket.core.componentanalysis.CADataBranch;
import info.openrocket.core.componentanalysis.CADataType;
import info.openrocket.core.componentanalysis.CADomainDataType;
import info.openrocket.core.componentanalysis.CAParameters;
import info.openrocket.core.logging.Warning;
import info.openrocket.core.logging.WarningSet;
import info.openrocket.core.document.Simulation;
import info.openrocket.core.rocketcomponent.AxialStage;
import info.openrocket.core.rocketcomponent.RocketComponent;
import info.openrocket.core.simulation.FlightData;
import info.openrocket.core.simulation.FlightDataBranch;
import info.openrocket.core.simulation.FlightDataType;
import info.openrocket.core.simulation.FlightEvent;
import info.openrocket.core.unit.Unit;
import info.openrocket.core.util.StringUtils;
import info.openrocket.core.util.TextUtil;
public class CSVExport {
@ -51,7 +59,6 @@ public class CSVExport {
PrintWriter writer = null;
try {
writer = new PrintWriter(stream, false, StandardCharsets.UTF_8);
// Write the initial comments
@ -88,10 +95,66 @@ public class CSVExport {
}
}
private static void writeData(PrintWriter writer, FlightDataBranch branch,
FlightDataType[] fields, Unit[] units, String fieldSeparator, int decimalPlaces,
boolean isExponentialNotation,
boolean eventComments, String commentStarter) {
public static void exportCSV(OutputStream stream, CAParameters parameters, CADataBranch branch,
CADomainDataType domainDataType, CADataType[] fields,
Map<CADataType, List<RocketComponent>> components, Unit[] units,
String fieldSeparator, int decimalPlaces, boolean isExponentialNotation,
boolean analysisComments, boolean fieldDescriptions, String commentStarter) throws IOException {
if (fields.length != units.length) {
throw new IllegalArgumentException("fields and units lengths must be equal " +
"(" + fields.length + " vs " + units.length + ")");
}
if (fields.length != components.size()) {
throw new IllegalArgumentException("fields and components lengths must be equal " +
"(" + fields.length + " vs " + components.size() + ")");
}
PrintWriter writer = null;
try {
writer = new PrintWriter(stream, false, StandardCharsets.UTF_8);
// Component analysis comments
if (analysisComments) {
writeComponentAnalysisComments(writer, parameters, branch, domainDataType, fields, components,
fieldSeparator, commentStarter);
}
// Field names
if (fieldDescriptions) {
writer.print(prependComment(commentStarter, StringUtils.removeHTMLTags(domainDataType.getName())));
writer.print(fieldSeparator);
for (int i = 0; i < fields.length; i++) {
for (int j = 0; j < components.get(fields[i]).size(); j++) {
writer.print(StringUtils.removeHTMLTags(fields[i].getName()) +
" (" + components.get(fields[i]).get(j).getName() + ") (" +
units[i].getUnit() + ")");
if (i < fields.length - 1) {
writer.print(fieldSeparator);
} else if (j < components.get(fields[i]).size() - 1) {
writer.print(fieldSeparator);
}
}
}
writer.println();
}
writeData(writer, branch, domainDataType, fields, components, units, fieldSeparator, decimalPlaces,
isExponentialNotation);
} finally {
if (writer != null) {
try {
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void writeData(PrintWriter writer, FlightDataBranch branch, FlightDataType[] fields, Unit[] units,
String fieldSeparator, int decimalPlaces, boolean isExponentialNotation,
boolean eventComments, String commentStarter) {
// Time variable
List<Double> time = branch.get(FlightDataType.TYPE_TIME);
@ -120,7 +183,6 @@ public class CSVExport {
// Loop over all data points
for (int pos = 0; pos < n; pos++) {
// Check for events to store
if (eventComments && time != null) {
double t = time.get(pos);
@ -142,7 +204,6 @@ public class CSVExport {
}
}
writer.println();
}
// Store any remaining events
@ -152,13 +213,54 @@ public class CSVExport {
eventPosition++;
}
}
}
private static void writeData(PrintWriter writer, CADataBranch branch, CADomainDataType domainDataType,
CADataType[] fields, Map<CADataType, List<RocketComponent>> components, Unit[] units,
String fieldSeparator, int decimalPlaces, boolean isExponentialNotation) {
List<Double> domainValues = branch.get(domainDataType);
int n = domainValues != null ? domainValues.size() : branch.getLength();
// List of field values
List<List<Double>> fieldValues = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
Unit unit = units[i];
for (RocketComponent c : components.get(fields[i])) {
List<Double> values = branch.get(fields[i], c);
// Convert the values to the correct unit
values.replaceAll(unit::toUnit);
fieldValues.add(values);
}
}
// Loop over all data points
for (int pos = 0; pos < n; pos++) {
// Store domain type
if (domainValues != null) {
writer.print(TextUtil.doubleToString(domainValues.get(pos), decimalPlaces, isExponentialNotation));
writer.print(fieldSeparator);
}
// Store CSV line
for (int i = 0; i < fieldValues.size(); i++) {
double value = fieldValues.get(i).get(pos);
writer.print(TextUtil.doubleToString(value, decimalPlaces, isExponentialNotation));
if (i < fieldValues.size() - 1) {
writer.print(fieldSeparator);
}
}
writer.println();
}
}
private static void printEvent(PrintWriter writer, FlightEvent e,
String commentStarter) {
writer.println(commentStarter + " Event " + e.getType().name() +
" occurred at t=" + TextUtil.doubleToString(e.getTime()) + " seconds");
writer.println(prependComment(commentStarter, "Event " + e.getType().name() +
" occurred at t=" + TextUtil.doubleToString(e.getTime()) + " seconds"));
}
private static void writeSimulationComments(PrintWriter writer,
@ -193,23 +295,70 @@ public class CSVExport {
break;
}
writer.println(commentStarter + " " + line);
writer.println(prependComment(commentStarter,line));
writer.println(commentStarter + " " + branch.getLength() + " data points written for "
+ fields.length + " variables.");
writer.println(prependComment(commentStarter, branch.getLength() + " data points written for "
+ fields.length + " variables."));
if (data == null) {
writer.println(commentStarter + " No simulation data available.");
writer.println(prependComment(commentStarter, "No simulation data available."));
return;
}
WarningSet warnings = data.getWarningSet();
if (!warnings.isEmpty()) {
writer.println(commentStarter + " Simulation warnings:");
writer.println(prependComment(commentStarter,"Simulation warnings:"));
for (Warning w : warnings) {
writer.println(commentStarter + " " + w.toString());
writer.println(prependComment(commentStarter, " " + w.toString()));
}
}
}
private static void writeComponentAnalysisComments(PrintWriter writer, CAParameters parameters, CADataBranch branch,
CADomainDataType domainDataType, CADataType[] fields,
Map<CADataType, List<RocketComponent>> components,
String fieldSeparator, String commentStarter) {
StringBuilder line = new StringBuilder(prependComment(commentStarter, "Parameters:")).append(fieldSeparator);
// TODO: use proper units for the parameters
if (domainDataType != CADomainDataType.WIND_DIRECTION) {
line.append("Wind direction:").append(fieldSeparator);
line.append(parameters.getTheta()).append("°").append(fieldSeparator);
}
if (domainDataType != CADomainDataType.AOA) {
line.append("Angle of attack:").append(fieldSeparator);
line.append(parameters.getAOA()).append("°").append(fieldSeparator);
}
if (domainDataType != CADomainDataType.MACH) {
line.append("Mach:").append(fieldSeparator);
line.append(parameters.getMach()).append(fieldSeparator);
}
if (domainDataType != CADomainDataType.ROLL_RATE) {
line.append("Roll rate:").append(fieldSeparator);
line.append(parameters.getRollRate()).append("°/s").append(fieldSeparator);
}
line.append("Active stages:").append(fieldSeparator);
List<AxialStage> activeStages = parameters.getSelectedConfiguration().getActiveStages();
for (AxialStage stage: activeStages) {
line.append(stage.getName()).append(fieldSeparator);
}
line.append("Flight configuration:").append(fieldSeparator);
line.append(parameters.getSelectedConfiguration().getName());
writer.println(line);
int nrOfVariables = 0;
for (CADataType t : fields) {
nrOfVariables += components.get(t).size();
}
writer.println(prependComment(commentStarter, branch.getLength() + " data points written for "
+ nrOfVariables + " variables."));
}
private static String prependComment(String commentStarter, String comment) {
return commentStarter + " " + comment;
}
}

View File

@ -104,4 +104,7 @@ public class StringUtils {
return sb.toString();
}
public static String removeHTMLTags(String input) {
return input.replaceAll("<[^>]*>", "");
}
}

View File

@ -968,6 +968,8 @@ CAPlotExportDialog.tab.Export = Export
! CAExportPanel
CAExportPanel.Col.Components = Components
CAExportPanel.checkbox.Includecadesc = Include component analysis description
CAExportPanel.checkbox.ttip.Includecadesc = Include information at the beginning of the file describing the component analysis.
! CADataTypeGroup
CADataTypeGroup.DOMAIN = Domain Parameter

View File

@ -1,5 +1,6 @@
package info.openrocket.swing.gui.dialogs.componentanalysis;
import info.openrocket.core.componentanalysis.CADataBranch;
import info.openrocket.core.componentanalysis.CADataType;
import info.openrocket.core.l10n.Translator;
import info.openrocket.core.rocketcomponent.RocketComponent;
@ -7,6 +8,7 @@ import info.openrocket.core.startup.Application;
import info.openrocket.core.unit.Unit;
import info.openrocket.swing.gui.components.CsvOptionPanel;
import info.openrocket.swing.gui.util.FileHelper;
import info.openrocket.swing.gui.util.SaveCSVWorker;
import info.openrocket.swing.gui.util.SwingPreferences;
import info.openrocket.swing.gui.widgets.CSVExportPanel;
import info.openrocket.swing.gui.widgets.SaveFileChooser;
@ -19,6 +21,7 @@ import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
@ -41,15 +44,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class CAExportPanel extends CSVExportPanel<CADataType> {
private static final long serialVersionUID = 4423905472892675964L;
private static final Translator trans = Application.getTranslator();
private static final int OPTION_FIELD_DESCRIPTIONS = 0;
private static final int FIXED_COMPONENT_COLUMN_WIDTH = 500;
private static final int OPTION_COMPONENT_ANALYSIS_COMMENTS = 0;
private static final int OPTION_FIELD_DESCRIPTIONS = 1;
private final List<Map<RocketComponent, Boolean>> selectedComponents;
private final ComponentAnalysisPlotExportPanel parent;
private CAExportPanel(ComponentAnalysisPlotExportPanel parent, CADataType[] types,
boolean[] selected, CsvOptionPanel csvOptions, Component... extraComponents) {
super(types, selected, csvOptions, true, extraComponents);
this.parent = parent;
selectedComponents = new ArrayList<>(types.length);
Map<RocketComponent, Boolean> componentSelectedMap;
List<RocketComponent> components;
@ -80,6 +88,8 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
}
CsvOptionPanel csvOptions = new CsvOptionPanel(CAExportPanel.class, false,
trans.get("CAExportPanel.checkbox.Includecadesc"),
trans.get("CAExportPanel.checkbox.ttip.Includecadesc"),
trans.get("SimExpPan.checkbox.Includefielddesc"),
trans.get("SimExpPan.checkbox.ttip.Includefielddesc"));
@ -94,9 +104,16 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
table.getColumnModel().getColumn(1).setCellRenderer(new LeftAlignedRenderer());
table.getColumnModel().getColumn(2).setCellRenderer(new LeftAlignedRenderer());
ComponentCheckBoxPanel.ComponentSelectionListener listener = (newStates) -> {
int row = table.getSelectedRow();
if (row != -1) {
selectedComponents.set(row, newStates);
}
};
TableColumn componentColumn = table.getColumnModel().getColumn(3);
componentColumn.setCellRenderer(new ComponentCheckBoxRenderer());
componentColumn.setCellEditor(new ComponentCheckBoxEditor());
componentColumn.setCellRenderer(new ComponentCheckBoxRenderer(listener));
componentColumn.setCellEditor(new ComponentCheckBoxEditor(listener));
componentColumn.setPreferredWidth(FIXED_COMPONENT_COLUMN_WIDTH);
// Set specific client properties for FlatLaf
@ -105,6 +122,8 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
@Override
public boolean doExport() {
CADataBranch branch = this.parent.runParameterSweep();
JFileChooser chooser = new SaveFileChooser();
chooser.setFileFilter(FileHelper.CSV_FILTER);
chooser.setCurrentDirectory(((SwingPreferences) Application.getPreferences()).getDefaultDirectory());
@ -126,7 +145,8 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
String fieldSep = csvOptions.getFieldSeparator();
int decimalPlaces = csvOptions.getDecimalPlaces();
boolean isExponentialNotation = csvOptions.isExponentialNotation();
boolean fieldComment = csvOptions.getSelectionOption(OPTION_FIELD_DESCRIPTIONS);
boolean analysisComments = csvOptions.getSelectionOption(OPTION_COMPONENT_ANALYSIS_COMMENTS);
boolean fieldDescriptions = csvOptions.getSelectionOption(OPTION_FIELD_DESCRIPTIONS);
csvOptions.storePreferences();
// Store preferences and export
@ -150,6 +170,22 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
}
}
Map<CADataType, List<RocketComponent>> components = new HashMap<>();
// Iterate through the table to get selected items
for (int i = 0; i < selected.length; i++) {
if (!selected[i]) {
continue;
}
for (Map.Entry<RocketComponent, Boolean> entry : selectedComponents.get(i).entrySet()) {
if (entry.getValue()) {
CADataType type = types[i];
List<RocketComponent> typeComponents = components.getOrDefault(type, new ArrayList<>());
typeComponents.add(entry.getKey());
components.put(type, typeComponents);
}
}
}
if (fieldSep.equals(SPACE)) {
fieldSep = " ";
} else if (fieldSep.equals(TAB)) {
@ -157,9 +193,9 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
}
/*SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, decimalPlaces,
isExponentialNotation, commentChar, simulationComment, fieldComment, eventComment,
SwingUtilities.getWindowAncestor(this));*/
SaveCSVWorker.exportCAData(file, parent.getParameters(), branch, parent.getSelectedParameter(), fieldTypes,
components, fieldUnits, fieldSep, decimalPlaces, isExponentialNotation, commentChar, analysisComments,
fieldDescriptions, SwingUtilities.getWindowAncestor(this));
return true;
}
@ -256,6 +292,11 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
private static class ComponentCheckBoxRenderer implements TableCellRenderer {
private ComponentCheckBoxPanel panel;
private final ComponentCheckBoxPanel.ComponentSelectionListener listener;
public ComponentCheckBoxRenderer(ComponentCheckBoxPanel.ComponentSelectionListener listener) {
this.listener = listener;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
@ -270,6 +311,7 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
if (panel == null) {
panel = new ComponentCheckBoxPanel(componentMap);
panel.setComponentSelectionListener(listener);
} else {
panel.updateComponentStates(componentMap);
}
@ -284,7 +326,6 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
private static class ComponentCheckBoxPanel extends JPanel {
private final Map<RocketComponent, JCheckBox> checkBoxMap = new HashMap<>();
private final ItemListener checkBoxListener;
private final AtomicBoolean updatingState = new AtomicBoolean(false);
private Color gridColor = Color.GRAY;
private boolean isSelected = false;
@ -297,20 +338,6 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
gbc.weightx = 1.0;
gbc.insets = new Insets(2, 2, 2, 2);
checkBoxListener = e -> {
if (updatingState.get()) return;
JCheckBox source = (JCheckBox) e.getSource();
if (e.getStateChange() == ItemEvent.DESELECTED) {
if (checkBoxMap.values().stream().noneMatch(JCheckBox::isSelected)) {
source.setSelected(true);
}
}
// Notify the table that the value has changed
firePropertyChange("value", null, getComponentStates());
};
createCheckBoxes(componentMap, gbc);
// Add an empty component to push everything to the top-left
@ -405,10 +432,44 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
g.fillRect(0, 0, getWidth(), getHeight() - 1);
}
}
public interface ComponentSelectionListener {
void onComponentSelectionChanged(Map<RocketComponent, Boolean> newStates);
}
private ComponentSelectionListener listener;
public void setComponentSelectionListener(ComponentSelectionListener listener) {
this.listener = listener;
}
private final ItemListener checkBoxListener = e -> {
if (updatingState.get()) return;
JCheckBox source = (JCheckBox) e.getSource();
if (e.getStateChange() == ItemEvent.DESELECTED) {
if (checkBoxMap.values().stream().noneMatch(JCheckBox::isSelected)) {
source.setSelected(true);
}
}
// Notify the listener of the change
if (listener != null) {
listener.onComponentSelectionChanged(getComponentStates());
}
// Notify the table that the value has changed
firePropertyChange("value", null, getComponentStates());
};
}
private static class ComponentCheckBoxEditor extends AbstractCellEditor implements TableCellEditor {
private ComponentCheckBoxPanel panel;
private final ComponentCheckBoxPanel.ComponentSelectionListener listener;
public ComponentCheckBoxEditor(ComponentCheckBoxPanel.ComponentSelectionListener listener) {
this.listener = listener;
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
@ -424,6 +485,7 @@ public class CAExportPanel extends CSVExportPanel<CADataType> {
if (panel == null) {
panel = new ComponentCheckBoxPanel(componentMap);
panel.setComponentSelectionListener(listener);
} else {
panel.updateComponentStates(componentMap);
}

View File

@ -218,6 +218,10 @@ public class ComponentAnalysisPlotExportPanel extends JPanel {
return (CADomainDataType) parameterSelector.getSelectedItem();
}
public CAParameters getParameters() {
return parameters;
}
private void updateModels(CADomainDataType type) {
if (type == null) {
throw new IllegalArgumentException("CADomainDataType cannot be null");

View File

@ -9,6 +9,7 @@ import info.openrocket.core.l10n.Translator;
import info.openrocket.core.simulation.DataBranch;
import info.openrocket.core.simulation.DataType;
import info.openrocket.core.startup.Application;
import info.openrocket.core.util.StringUtils;
public abstract class Util {
private static final Translator trans = Application.getTranslator();
@ -81,7 +82,7 @@ public abstract class Util {
public static String formatHTMLString(String input) {
// TODO: Use AttributeString to format the string
// Remove the HTML-like tags from the final string
return input.replaceAll("<sub>|</sub>|<sup>|</sup>|<html>|</html>", "");
return StringUtils.removeHTMLTags(input);
}
public static Color getPlotColor(int index) {

View File

@ -159,7 +159,7 @@ public class SimulationExportPanel extends CSVExportPanel<FlightDataType> {
}
SaveCSVWorker.export(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, decimalPlaces,
SaveCSVWorker.exportSimulationData(file, simulation, branch, fieldTypes, fieldUnits, fieldSep, decimalPlaces,
isExponentialNotation, commentChar, simulationComment, fieldComment, eventComment,
SwingUtilities.getWindowAncestor(this));

View File

@ -5,11 +5,18 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import info.openrocket.core.componentanalysis.CADataBranch;
import info.openrocket.core.componentanalysis.CADataType;
import info.openrocket.core.componentanalysis.CADomainDataType;
import info.openrocket.core.componentanalysis.CAParameters;
import info.openrocket.core.rocketcomponent.RocketComponent;
import info.openrocket.swing.gui.dialogs.SwingWorkerDialog;
import info.openrocket.core.document.Simulation;
@ -21,34 +28,47 @@ import info.openrocket.core.unit.Unit;
import info.openrocket.core.util.BugException;
import info.openrocket.core.util.TextUtil;
public class SaveCSVWorker extends SwingWorker<Void, Void> {
private static final int BYTES_PER_FIELD_PER_POINT = 7;
private final File file;
private final Simulation simulation;
private final FlightDataBranch branch;
private final FlightDataType[] fields;
private final Unit[] units;
private final String fieldSeparator;
private final int decimalPlaces;
private final boolean isExponentialNotation;
private final String commentStarter;
private final boolean simulationComments;
private final boolean fieldComments;
private final boolean eventComments;
// Simulation-specific fields
private Simulation simulation;
private FlightDataBranch flightDataBranch;
private FlightDataType[] flightDataFields;
private Unit[] flightDataUnits;
private boolean simulationComments;
private boolean fieldComments;
private boolean eventComments;
// CA-specific fields
private CAParameters caParameters;
private CADataBranch caDataBranch;
private CADomainDataType caDomainDataType;
private CADataType[] caDataFields;
private Map<CADataType, List<RocketComponent>> caComponents;
private Unit[] caUnits;
private boolean analysisComments;
private boolean fieldDescriptions;
private boolean isCAData;
// Constructor for simulation data
public SaveCSVWorker(File file, Simulation simulation, FlightDataBranch branch,
FlightDataType[] fields, Unit[] units, String fieldSeparator, int decimalPlaces,
boolean isExponentialNotation, String commentStarter, boolean simulationComments,
boolean fieldComments, boolean eventComments) {
FlightDataType[] fields, Unit[] units, String fieldSeparator, int decimalPlaces,
boolean isExponentialNotation, String commentStarter, boolean simulationComments,
boolean fieldComments, boolean eventComments) {
this.file = file;
this.simulation = simulation;
this.branch = branch;
this.fields = fields;
this.units = units;
this.flightDataBranch = branch;
this.flightDataFields = fields;
this.flightDataUnits = units;
this.fieldSeparator = fieldSeparator;
this.decimalPlaces = decimalPlaces;
this.isExponentialNotation = isExponentialNotation;
@ -56,90 +76,145 @@ public class SaveCSVWorker extends SwingWorker<Void, Void> {
this.simulationComments = simulationComments;
this.fieldComments = fieldComments;
this.eventComments = eventComments;
this.isCAData = false;
}
// Constructor for CA data
public SaveCSVWorker(File file, CAParameters parameters, CADataBranch branch,
CADomainDataType domainDataType, CADataType[] fields,
Map<CADataType, List<RocketComponent>> components, Unit[] units,
String fieldSeparator, int decimalPlaces, boolean isExponentialNotation,
String commentStarter, boolean analysisComments, boolean fieldDescriptions) {
this.file = file;
this.caParameters = parameters;
this.caDataBranch = branch;
this.caDomainDataType = domainDataType;
this.caDataFields = fields;
this.caComponents = components;
this.caUnits = units;
this.fieldSeparator = fieldSeparator;
this.decimalPlaces = decimalPlaces;
this.isExponentialNotation = isExponentialNotation;
this.commentStarter = commentStarter;
this.analysisComments = analysisComments;
this.fieldDescriptions = fieldDescriptions;
this.isCAData = true;
}
@Override
protected Void doInBackground() throws Exception {
int estimate = BYTES_PER_FIELD_PER_POINT * fields.length * branch.getLength();
int estimate = BYTES_PER_FIELD_PER_POINT * (isCAData ? caDataFields.length : flightDataFields.length) *
(isCAData ? caDataBranch.getLength() : flightDataBranch.getLength());
estimate = Math.max(estimate, 1000);
// Create the ProgressOutputStream that provides progress estimates
@SuppressWarnings("resource")
ProgressOutputStream os = new ProgressOutputStream(
new BufferedOutputStream(new FileOutputStream(file)),
try (ProgressOutputStream os = new ProgressOutputStream(
new BufferedOutputStream(new FileOutputStream(file)),
estimate, this) {
@Override
protected void setProgress(int progress) {
SaveCSVWorker.this.setProgress(progress);
}
};
try {
CSVExport.exportCSV(os, simulation, branch, fields, units, fieldSeparator, decimalPlaces, isExponentialNotation,
commentStarter, simulationComments, fieldComments, eventComments);
} finally {
try {
os.close();
} catch (Exception e) {
Application.getExceptionHandler().handleErrorCondition("Error closing file", e);
}) {
if (isCAData) {
CSVExport.exportCSV(os, caParameters, caDataBranch, caDomainDataType, caDataFields, caComponents, caUnits,
fieldSeparator, decimalPlaces, isExponentialNotation, analysisComments, fieldDescriptions, commentStarter);
} else {
CSVExport.exportCSV(os, simulation, flightDataBranch, flightDataFields, flightDataUnits, fieldSeparator,
decimalPlaces, isExponentialNotation, commentStarter, simulationComments, fieldComments, eventComments);
}
} catch (Exception e) {
Application.getExceptionHandler().handleErrorCondition("Error writing file", e);
}
return null;
}
public static boolean export(File file, Simulation simulation, FlightDataBranch branch,
public static boolean exportSimulationData(File file, Simulation simulation, FlightDataBranch branch,
FlightDataType[] fields, Unit[] units, String fieldSeparator,
String commentStarter, boolean simulationComments,
boolean fieldComments, boolean eventComments, Window parent) {
return export(file, simulation, branch, fields, units, fieldSeparator, TextUtil.DEFAULT_DECIMAL_PLACES, true,
return exportSimulationData(file, simulation, branch, fields, units, fieldSeparator, TextUtil.DEFAULT_DECIMAL_PLACES, true,
commentStarter, simulationComments, fieldComments, eventComments, parent);
}
/**
* Exports a CSV file using a progress dialog if necessary.
*
* @return <code>true</code> if the save was successful, <code>false</code> otherwise.
*/
public static boolean export(File file, Simulation simulation, FlightDataBranch branch,
FlightDataType[] fields, Unit[] units, String fieldSeparator, int decimalPlaces,
boolean isExponentialNotation, String commentStarter, boolean simulationComments,
boolean fieldComments, boolean eventComments, Window parent) {
public static boolean exportSimulationData(File file, Simulation simulation, FlightDataBranch branch,
FlightDataType[] fields, Unit[] units, String fieldSeparator, int decimalPlaces,
boolean isExponentialNotation, String commentStarter, boolean simulationComments,
boolean fieldComments, boolean eventComments, Window parent) {
SaveCSVWorker worker = new SaveCSVWorker(file, simulation, branch, fields, units,
fieldSeparator, decimalPlaces, isExponentialNotation, commentStarter, simulationComments,
fieldComments, eventComments);
if (!SwingWorkerDialog.runWorker(parent, "Exporting flight data",
"Writing " + file.getName() + "...", worker)) {
// User cancelled the save
file.delete();
return false;
}
try {
if (!SwingWorkerDialog.runWorker(parent, "Exporting flight data",
"Writing " + file.getName() + "...", worker)) {
// User cancelled the save
file.delete();
return false;
}
try {
worker.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
JOptionPane.showMessageDialog(parent, new String[] {
"An I/O error occurred while saving:",
e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
return false;
JOptionPane.showMessageDialog(parent, new String[] {
"An I/O error occurred while saving:",
e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
return false;
} else {
throw new BugException("Unknown error when saving file", e);
}
} catch (InterruptedException e) {
throw new BugException("EDT was interrupted", e);
}
return true;
}
}
// New export method for CA data
public static boolean exportCAData(File file, CAParameters parameters, CADataBranch branch,
CADomainDataType domainDataType, CADataType[] fields,
Map<CADataType, List<RocketComponent>> components, Unit[] units,
String fieldSeparator, int decimalPlaces, boolean isExponentialNotation,
String commentStarter, boolean analysisComments, boolean fieldDescriptions,
Window parent) {
SaveCSVWorker worker = new SaveCSVWorker(file, parameters, branch, domainDataType, fields, components, units,
fieldSeparator, decimalPlaces, isExponentialNotation, commentStarter, analysisComments, fieldDescriptions);
if (!SwingWorkerDialog.runWorker(parent, "Exporting component analysis data",
"Writing " + file.getName() + "...", worker)) {
// User cancelled the save
file.delete();
return false;
}
try {
worker.get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
JOptionPane.showMessageDialog(parent, new String[] {
"An I/O error occurred while saving:",
e.getMessage() }, "Saving failed", JOptionPane.ERROR_MESSAGE);
return false;
} else {
throw new BugException("Unknown error when saving file", e);
}
} catch (InterruptedException e) {
throw new BugException("EDT was interrupted", e);
}
return true;
}
}