[#2525] Allow component analysis to be exported to a CSV file
This commit is contained in:
parent
1840bd4c0e
commit
525190b37f
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -104,4 +104,7 @@ public class StringUtils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String removeHTMLTags(String input) {
|
||||
return input.replaceAll("<[^>]*>", "");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user