Implemented a DeploymentConfiguration object which includes the values

used to determine the deployment of a recovery device.  The DeployEvent
enum was moved into this class.  Added a map of configuration id strings
to DeploymentConfiguration objects in the RecoveryDevice.  Modified
various setter/getters in RecoveryDevice to support a "default"
configuration defined in the device's configuration dialog.  Modified
BasicSimulationEngine to use the DeploymentConfiguration based on the
selected configuration.

Added to the FlightConfiguration dialogs to support editing these
configuration.

Missing are serialization of the map in ork files.
This commit is contained in:
kruland2607 2012-10-16 22:13:37 -05:00
parent e18b081273
commit 3a8d2d5560
15 changed files with 582 additions and 116 deletions

View File

@ -185,10 +185,15 @@ edtmotorconfdlg.selectcomp = <html>Select which components function as motor mou
edtmotorconfdlg.lbl.Motorconfig = <html><b>Motor configurations:</b>
edtmotorconfdlg.lbl.Configname = Configuration name:
edtmotorconfdlg.lbl.Motortab = Motors
edtmotorconfdlg.lbl.Recoverytab = Recovery
edtmotorconfdlg.tbl.None = None
edtmotorconfdlg.tbl.Motorheader = Motor
edtmotorconfdlg.tbl.Mountheader = Motor Mount
edtmotorconfdlg.tbl.Ignitionheader = Ignition
edtmotorconfdlg.but.Resetdeployment = Reset to default
edtmotorconfdlg.but.Selectdeployment = Select deployment
edtmotorconfdlg.tbl.Recoveryheader = Recovery Device
edtmotorconfdlg.tbl.Deploymentheader = Deployment
! Example design dialog
exdesigndlg.but.open = Open

View File

@ -17,10 +17,10 @@ import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.document.StorageOptions;
import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.RecoveryDevice.DeployEvent;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
@ -250,7 +250,7 @@ public class OpenRocketSaver extends RocketSaver {
// Search for recovery device deployment type LOWER_STAGE_SEPARATION (version 1.5)
for (RocketComponent c : document.getRocket()) {
if (c instanceof RecoveryDevice) {
if (((RecoveryDevice) c).getDeployEvent() == DeployEvent.LOWER_STAGE_SEPARATION) {
if (((RecoveryDevice) c).getDefaultDeployEvent() == DeployEvent.LOWER_STAGE_SEPARATION) {
return FILE_VERSION_DIVISOR + 5;
}
}

View File

@ -35,6 +35,7 @@ import net.sf.openrocket.rocketcomponent.Bulkhead;
import net.sf.openrocket.rocketcomponent.CenteringRing;
import net.sf.openrocket.rocketcomponent.ClusterConfiguration;
import net.sf.openrocket.rocketcomponent.Clusterable;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
import net.sf.openrocket.rocketcomponent.EngineBlock;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
@ -445,13 +446,13 @@ class DocumentConfig {
Reflection.findMethod(RecoveryDevice.class, "setCD", double.class),
"auto",
Reflection.findMethod(RecoveryDevice.class, "setCDAutomatic", boolean.class)));
setters.put("RecoveryDevice:deployevent", new EnumSetter<RecoveryDevice.DeployEvent>(
Reflection.findMethod(RecoveryDevice.class, "setDeployEvent", RecoveryDevice.DeployEvent.class),
RecoveryDevice.DeployEvent.class));
setters.put("RecoveryDevice:deployevent", new EnumSetter<DeployEvent>(
Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployEvent", DeployEvent.class),
DeployEvent.class));
setters.put("RecoveryDevice:deployaltitude", new DoubleSetter(
Reflection.findMethod(RecoveryDevice.class, "setDeployAltitude", double.class)));
Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployAltitude", double.class)));
setters.put("RecoveryDevice:deploydelay", new DoubleSetter(
Reflection.findMethod(RecoveryDevice.class, "setDeployDelay", double.class)));
Reflection.findMethod(RecoveryDevice.class, "setDefaultDeployDelay", double.class)));
setters.put("RecoveryDevice:material", new MaterialSetter(
Reflection.findMethod(RecoveryDevice.class, "setMaterial", Material.class),
Material.Type.SURFACE));
@ -876,7 +877,6 @@ class ComponentParameterHandler extends AbstractElementHandler {
return new MotorConfigurationHandler((Rocket) component, context);
}
return PlainTextHandler.INSTANCE;
}

View File

@ -19,10 +19,12 @@ public class RecoveryDeviceSaver extends MassObjectSaver {
else
elements.add("<cd>" + dev.getCD() + "</cd>");
elements.add("<deployevent>" + dev.getDeployEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + "</deployevent>");
elements.add("<deployaltitude>" + dev.getDeployAltitude() + "</deployaltitude>");
elements.add("<deploydelay>" + dev.getDeployDelay() + "</deploydelay>");
elements.add("<deployevent>" + dev.getDefaultDeployEvent().name().toLowerCase(Locale.ENGLISH).replace("_", "") + "</deployevent>");
elements.add("<deployaltitude>" + dev.getDefaultDeployAltitude() + "</deployaltitude>");
elements.add("<deploydelay>" + dev.getDefaultDeployDelay() + "</deploydelay>");
elements.add(materialParam(dev.getMaterial()));
// FIXME - add mapping.
}
}

View File

@ -0,0 +1,48 @@
package net.sf.openrocket.gui.adaptors;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
public class BasicEnumModel<T extends Enum<T>> extends AbstractListModel implements ComboBoxModel {
private final String nullText;
private final Enum<T>[] values;
private Enum<T> currentValue = null;
public BasicEnumModel( Class<? extends Enum<T>> clazz ) {
this(clazz, null);
}
@SuppressWarnings("unchecked")
public BasicEnumModel(Class<? extends Enum<T>> clazz, String nullText) {
this.values = clazz.getEnumConstants();
this.nullText = nullText;
}
@Override
public void setSelectedItem(Object anItem) {
currentValue = (Enum<T>) anItem;
}
@Override
public Object getSelectedItem() {
if (currentValue==null)
return nullText;
return currentValue;
}
@Override
public Object getElementAt(int index) {
if (values[index] == null)
return nullText;
return values[index];
}
@Override
public int getSize() {
return values.length;
}
}

View File

@ -192,14 +192,14 @@ public class ParachuteConfig extends RecoveryDeviceConfig {
//// Deploys at:
panel.add(new JLabel(trans.get("ParachuteCfg.lbl.Deploysat")), "");
combo = new JComboBox(new EnumModel<IgnitionEvent>(component, "DeployEvent"));
combo = new JComboBox(new EnumModel<IgnitionEvent>(component, "DefaultDeployEvent"));
panel.add(combo, "spanx 3, growx, wrap");
// ... and delay
//// plus
panel.add(new JLabel(trans.get("ParachuteCfg.lbl.plusdelay")), "right");
m = new DoubleModel(component, "DeployDelay", 0);
m = new DoubleModel(component, "DefaultDeployDelay", 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin,3));
panel.add(spin, "spanx, split");
@ -212,7 +212,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig {
altitudeComponents.add(label);
panel.add(label);
m = new DoubleModel(component, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0);
m = new DoubleModel(component, "DefaultDeployAltitude", UnitGroup.UNITS_DISTANCE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));

View File

@ -6,6 +6,7 @@ import java.util.List;
import javax.swing.JComponent;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -27,8 +28,7 @@ public abstract class RecoveryDeviceConfig extends RocketComponentConfig {
if (altitudeComponents == null)
return;
boolean enabled = (((RecoveryDevice) component).getDeployEvent()
== RecoveryDevice.DeployEvent.ALTITUDE);
boolean enabled = (((RecoveryDevice) component).getDefaultDeployEvent() == DeployEvent.ALTITUDE);
for (JComponent c : altitudeComponents) {
c.setEnabled(enabled);

View File

@ -193,14 +193,14 @@ public class StreamerConfig extends RecoveryDeviceConfig {
//// Deploys at:
panel.add(new JLabel(trans.get("StreamerCfg.lbl.Deploysat")), "");
combo = new JComboBox(new EnumModel<IgnitionEvent>(component, "DeployEvent"));
combo = new JComboBox(new EnumModel<IgnitionEvent>(component, "DefaultDeployEvent"));
panel.add(combo, "spanx 3, growx, wrap");
// ... and delay
//// plus
panel.add(new JLabel(trans.get("StreamerCfg.lbl.plusdelay")), "right");
m = new DoubleModel(component, "DeployDelay", 0);
m = new DoubleModel(component, "DefaultDeployDelay", 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin,3));
panel.add(spin, "spanx, split");
@ -213,7 +213,7 @@ public class StreamerConfig extends RecoveryDeviceConfig {
altitudeComponents.add(label);
panel.add(label);
m = new DoubleModel(component, "DeployAltitude", UnitGroup.UNITS_DISTANCE, 0);
m = new DoubleModel(component, "DefaultDeployAltitude", UnitGroup.UNITS_DISTANCE, 0);
spin = new JSpinner(m.getSpinnerModel());
spin.setEditor(new SpinnerEditor(spin));

View File

@ -122,11 +122,14 @@ public class FlightConfigurationDialog extends JDialog {
//// Tabs for advanced view.
JTabbedPane tabs = new JTabbedPane();
panel.add( tabs, "spanx, w 700lp, h 500lp, wrap");
//// Motor tabs
JComponent motorTab = makeMotorTab();
tabs.add(trans.get("edtmotorconfdlg.lbl.Motortab"), motorTab);
panel.add( tabs, "spanx, w 700lp, h 500lp, wrap");
//// Recovery tab
tabs.add(trans.get("edtmotorconfdlg.lbl.Recoverytab"), new RecoveryConfigurationPanel(this,rocket));
//// Close button
JButton close = new JButton(trans.get("dlg.but.close"));
@ -386,5 +389,5 @@ public class FlightConfigurationDialog extends JDialog {
return panel;
}
}

View File

@ -0,0 +1,168 @@
package net.sf.openrocket.gui.dialogs.flightconfiguration;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
public class RecoveryConfigurationPanel extends JPanel {
private final FlightConfigurationDialog flightConfigurationDialog;
private final Rocket rocket;
private final JButton selectDeploymentButton;
private final JButton resetDeploymentButton;
private RecoveryDevice selectedComponent;
RecoveryConfigurationPanel( FlightConfigurationDialog flightConfigurationDialog, Rocket rocket ) {
super( new MigLayout("fill") );
this.flightConfigurationDialog = flightConfigurationDialog;
this.rocket = rocket;
//// Recovery selection
JTable table = new JTable( new RecoveryTableModel() );
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setCellSelectionEnabled(true);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
JTable table = (JTable) e.getComponent();
int row = table.getSelectedRow();
int column = table.getSelectedColumn();
if ( row >= 0 & column == 1) {
selectedComponent = findRecoveryDevice(row);
} else {
selectedComponent = null;
}
if (e.getClickCount() == 1) {
// Single click updates selection
updateButtonState();
} else if (e.getClickCount() == 2) {
// Double-click edits
selectDeployment();
}
}
});
JScrollPane scroll = new JScrollPane(table);
this.add(scroll, "span, grow, wrap");
//// Select deployment
selectDeploymentButton = new JButton(FlightConfigurationDialog.trans.get("edtmotorconfdlg.but.Selectdeployment"));
selectDeploymentButton.setEnabled(false);
selectDeploymentButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectDeployment();
}
});
this.add(selectDeploymentButton, "skip, split, sizegroup button");
//// Reset deployment
resetDeploymentButton = new JButton(FlightConfigurationDialog.trans.get("edtmotorconfdlg.but.Resetdeployment"));
resetDeploymentButton.setEnabled(false);
resetDeploymentButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//resetDeployment;
}
});
this.add(resetDeploymentButton,"sizegroup button, wrap");
}
private void selectDeployment() {
JDialog d = new SelectDeploymentConfigDialog( flightConfigurationDialog, rocket, selectedComponent );
d.setVisible(true);
}
private void updateButtonState() {
boolean buttonsEnabled = selectedComponent != null;
selectDeploymentButton.setEnabled(buttonsEnabled);
resetDeploymentButton.setEnabled(buttonsEnabled);
}
private RecoveryDevice findRecoveryDevice( int count ) {
RecoveryDevice d = null;
Iterator<RocketComponent> it = rocket.iterator();
while( it.hasNext() && count >= 0 ) {
RocketComponent c = it.next();
if ( c instanceof RecoveryDevice ) {
d = (RecoveryDevice) c;
count--;
}
}
return d;
}
private class RecoveryTableModel extends AbstractTableModel {
@Override
public int getRowCount() {
int count = 0;
Iterator<RocketComponent> it = rocket.iterator();
while( it.hasNext() ) {
RocketComponent c = it.next();
if ( c instanceof RecoveryDevice ) {
count ++;
}
}
return count;
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
RecoveryDevice d = RecoveryConfigurationPanel.this.findRecoveryDevice(rowIndex);
switch ( columnIndex ) {
case 0:
return d.getName();
case 1:
DeploymentConfiguration deployConfig = d.getDeploymentConfiguration(rocket.getDefaultConfiguration().getMotorConfigurationID());
if ( deployConfig == null ) {
return "[" + d.getDefaultDeploymentConfiguration().toString() + "]";
} else {
return deployConfig.toString();
}
}
return null;
}
@Override
public String getColumnName(int column) {
switch ( column ) {
case 0:
return FlightConfigurationDialog.trans.get("edtmotorconfdlg.tbl.Recoveryheader");
case 1:
return FlightConfigurationDialog.trans.get("edtmotorconfdlg.tbl.Deploymentheader");
default:
return "";
}
}
}
}

View File

@ -0,0 +1,144 @@
package net.sf.openrocket.gui.dialogs.flightconfiguration;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.SpinnerEditor;
import net.sf.openrocket.gui.adaptors.BasicEnumModel;
import net.sf.openrocket.gui.adaptors.DoubleModel;
import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.MotorMount.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.unit.UnitGroup;
public class SelectDeploymentConfigDialog extends JDialog {
DeploymentConfiguration newConfiguration;
SelectDeploymentConfigDialog( JDialog parent, final Rocket rocket, final RecoveryDevice component ) {
super(parent);
super.setModal(true);
final String configId = rocket.getDefaultConfiguration().getMotorConfigurationID();
newConfiguration = component.getDeploymentConfiguration(configId);
if ( newConfiguration == null ) {
newConfiguration = component.getDefaultDeploymentConfiguration().clone();
} else {
// Clone the existing so cancel works. When the user selects OK, this configuration
// is put back in there.
newConfiguration = newConfiguration.clone();
}
JPanel panel = new JPanel(new MigLayout("fill"));
//// Deployment
//// Deploys at:
panel.add(new JLabel(FlightConfigurationDialog.trans.get("ParachuteCfg.lbl.Deploysat")), "");
final JComboBox event = new JComboBox(new BasicEnumModel<DeployEvent>(DeployEvent.class));
panel.add(event, "spanx 3, growx, wrap");
// ... and delay
//// plus
panel.add(new JLabel(FlightConfigurationDialog.trans.get("ParachuteCfg.lbl.plusdelay")), "right");
final DoubleModel delay = new DoubleModel(newConfiguration.getDeployDelay(), UnitGroup.UNITS_NONE, 0);
final JSpinner delaySpinner = new JSpinner( delay.getSpinnerModel() );
delaySpinner.setEditor(new SpinnerEditor(delaySpinner,3));
panel.add(delaySpinner, "spanx, split");
//// seconds
panel.add(new JLabel(FlightConfigurationDialog.trans.get("ParachuteCfg.lbl.seconds")), "wrap paragraph");
// Altitude:
JLabel label = new JLabel(FlightConfigurationDialog.trans.get("ParachuteCfg.lbl.Altitude"));
panel.add(label);
final DoubleModel alt = new DoubleModel(newConfiguration.getDeployAltitude(), UnitGroup.UNITS_DISTANCE, 0);
final JSpinner altSpinner = new JSpinner(alt.getSpinnerModel());
altSpinner.setEditor(new SpinnerEditor(altSpinner));
panel.add(altSpinner, "growx");
UnitSelector unit = new UnitSelector(alt);
panel.add(unit, "growx");
BasicSlider slider = new BasicSlider(alt.getSliderModel(100, 1000));
panel.add(slider, "w 100lp, wrap");
event.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if ( event.getSelectedItem() == DeployEvent.ALTITUDE ) {
delaySpinner.setEnabled(true);
altSpinner.setEnabled(true);
} else {
delaySpinner.setEnabled(false);
altSpinner.setEnabled(false);
}
}
});
// Set the value of the combo box at the end to take advantage of the action listener above.
event.setSelectedItem( newConfiguration.getDeployEvent() );
JButton okButton = new JButton("Ok");
okButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//// extract deployment type;
DeployEvent deployEvent = (DeployEvent) event.getSelectedItem();
newConfiguration.setDeployEvent(deployEvent);
//// extract deployment time;
double deployDelay = delay.getValue();
newConfiguration.setDeployDelay(deployDelay);
//// extract altitude;
double deployAltitude = alt.getValue();
newConfiguration.setDeployDelay(deployAltitude);
component.setDeploymentConfiguration(configId, newConfiguration);
SelectDeploymentConfigDialog.this.setVisible(false);
}
});
panel.add( okButton );
JButton cancel = new JButton("Cancel");
cancel.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SelectDeploymentConfigDialog.this.setVisible(false);
}
});
panel.add( cancel );
this.setContentPane(panel);
this.validate();
this.pack();
}
}

View File

@ -14,6 +14,7 @@ import net.sf.openrocket.optimization.general.OptimizationException;
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
import net.sf.openrocket.optimization.rocketoptimization.modifiers.GenericComponentModifier;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
@ -24,7 +25,6 @@ import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.Parachute;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.RecoveryDevice.DeployEvent;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Streamer;
@ -256,7 +256,7 @@ public class DefaultSimulationModifierService implements SimulationModifierServi
mod.setMaxValue(10);
modifiers.add(mod);
if (device.getDeployEvent() == DeployEvent.ALTITUDE) {
if (device.getDefaultDeployEvent() == DeployEvent.ALTITUDE) {
mod = new GenericComponentModifier(
trans.get("optimization.modifier.recoverydevice.deployAltitude"),
trans.get("optimization.modifier.recoverydevice.deployAltitude.desc"),

View File

@ -0,0 +1,138 @@
package net.sf.openrocket.rocketcomponent;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.Pair;
public class DeploymentConfiguration implements Cloneable {
private static final Translator trans = Application.getTranslator();
private DeployEvent deployEvent = DeployEvent.EJECTION;
private double deployAltitude = 200;
private double deployDelay = 0;
public boolean isActivationEvent( FlightEvent e, RocketComponent source ) {
return deployEvent.isActivationEvent(this, e, source);
}
public DeployEvent getDeployEvent() {
return deployEvent;
}
public void setDeployEvent(DeployEvent deployEvent) {
this.deployEvent = deployEvent;
}
public double getDeployAltitude() {
return deployAltitude;
}
public void setDeployAltitude(double deployAltitude) {
this.deployAltitude = deployAltitude;
}
public double getDeployDelay() {
return deployDelay;
}
public void setDeployDelay(double deployDelay) {
this.deployDelay = deployDelay;
}
@Override
public String toString() {
String description = deployEvent.toString();
if ( deployDelay > 0 ) {
description += " + " + deployDelay + "s";
}
if ( deployEvent == DeployEvent.ALTITUDE && deployAltitude != 0 ) {
description += " " + UnitGroup.UNITS_DISTANCE.fromUnit(deployAltitude);
}
return description;
}
@Override
public DeploymentConfiguration clone() {
DeploymentConfiguration that = new DeploymentConfiguration();
that.deployAltitude = this.deployAltitude;
that.deployDelay = this.deployDelay;
that.deployEvent = this.deployEvent;
return that;
}
public static enum DeployEvent {
LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) {
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
return e.getType() == FlightEvent.Type.LAUNCH;
}
},
EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) {
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.EJECTION_CHARGE)
return false;
RocketComponent charge = e.getSource();
return charge.getStageNumber() == source.getStageNumber();
}
},
APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) {
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
return e.getType() == FlightEvent.Type.APOGEE;
}
},
ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) {
@SuppressWarnings("unchecked")
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.ALTITUDE)
return false;
double alt = config.deployAltitude;
Pair<Double, Double> altitude = (Pair<Double, Double>) e.getData();
return (altitude.getU() >= alt) && (altitude.getV() <= alt);
}
},
LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) {
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.STAGE_SEPARATION)
return false;
int separation = e.getSource().getStageNumber();
int current = source.getStageNumber();
return (current + 1 == separation);
}
},
NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) {
@Override
public boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source) {
return false;
}
};
private final String description;
DeployEvent(String description) {
this.description = description;
}
public abstract boolean isActivationEvent(DeploymentConfiguration config, FlightEvent e, RocketComponent source);
@Override
public String toString() {
return description;
}
}
}

View File

@ -1,12 +1,14 @@
package net.sf.openrocket.rocketcomponent;
import java.util.HashMap;
import java.util.Map;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Pair;
/**
@ -24,78 +26,9 @@ import net.sf.openrocket.util.Pair;
public abstract class RecoveryDevice extends MassObject {
private static final Translator trans = Application.getTranslator();
public static enum DeployEvent {
LAUNCH(trans.get("RecoveryDevice.DeployEvent.LAUNCH")) {
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
return e.getType() == FlightEvent.Type.LAUNCH;
}
},
EJECTION(trans.get("RecoveryDevice.DeployEvent.EJECTION")) {
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.EJECTION_CHARGE)
return false;
RocketComponent charge = e.getSource();
return charge.getStageNumber() == source.getStageNumber();
}
},
APOGEE(trans.get("RecoveryDevice.DeployEvent.APOGEE")) {
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
return e.getType() == FlightEvent.Type.APOGEE;
}
},
ALTITUDE(trans.get("RecoveryDevice.DeployEvent.ALTITUDE")) {
@SuppressWarnings("unchecked")
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.ALTITUDE)
return false;
double alt = ((RecoveryDevice) source).getDeployAltitude();
Pair<Double, Double> altitude = (Pair<Double, Double>) e.getData();
return (altitude.getU() >= alt) && (altitude.getV() <= alt);
}
},
LOWER_STAGE_SEPARATION(trans.get("RecoveryDevice.DeployEvent.LOWER_STAGE_SEPARATION")) {
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
if (e.getType() != FlightEvent.Type.STAGE_SEPARATION)
return false;
int separation = e.getSource().getStageNumber();
int current = source.getStageNumber();
return (current + 1 == separation);
}
},
NEVER(trans.get("RecoveryDevice.DeployEvent.NEVER")) {
@Override
public boolean isActivationEvent(FlightEvent e, RocketComponent source) {
return false;
}
};
private final String description;
DeployEvent(String description) {
this.description = description;
}
public abstract boolean isActivationEvent(FlightEvent e, RocketComponent source);
@Override
public String toString() {
return description;
}
}
private Map<String,DeploymentConfiguration> deploymentConfigurations = new HashMap<String,DeploymentConfiguration>();
private DeployEvent deployEvent = DeployEvent.EJECTION;
private double deployAltitude = 200;
private double deployDelay = 0;
private DeploymentConfiguration defaultDeploymentConfig = new DeploymentConfiguration();
private double cd = Parachute.DEFAULT_CD;
private boolean cdAutomatic = true;
@ -175,44 +108,61 @@ public abstract class RecoveryDevice extends MassObject {
}
public DeployEvent getDeployEvent() {
return deployEvent;
/**
* Return the deployment configuration for this configurationId or null if using default.
*
* @param configID
* @return
*/
public DeploymentConfiguration getDeploymentConfiguration( String configID ) {
DeploymentConfiguration config = deploymentConfigurations.get(configID);
return config;
}
public void setDeployEvent(DeployEvent deployEvent) {
if (this.deployEvent == deployEvent)
public DeploymentConfiguration getDefaultDeploymentConfiguration() {
return defaultDeploymentConfig;
}
public void setDeploymentConfiguration( String configID, DeploymentConfiguration config ) {
deploymentConfigurations.put(configID, config);
}
public DeployEvent getDefaultDeployEvent() {
return defaultDeploymentConfig.getDeployEvent();
}
public void setDefaultDeployEvent(DeployEvent deployEvent) {
if (this.defaultDeploymentConfig.getDeployEvent() == deployEvent)
return;
this.deployEvent = deployEvent;
this.defaultDeploymentConfig.setDeployEvent(deployEvent);
fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
}
public double getDeployAltitude() {
return deployAltitude;
public double getDefaultDeployAltitude() {
return defaultDeploymentConfig.getDeployAltitude();
}
public void setDeployAltitude(double deployAltitude) {
if (MathUtil.equals(this.deployAltitude, deployAltitude))
public void setDefaultDeployAltitude(double deployAltitude) {
if (MathUtil.equals(getDefaultDeployAltitude(), deployAltitude))
return;
this.deployAltitude = deployAltitude;
if (getDeployEvent() == DeployEvent.ALTITUDE)
defaultDeploymentConfig.setDeployAltitude(deployAltitude);
if (getDefaultDeployEvent() == DeployEvent.ALTITUDE)
fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
else
fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE);
}
public double getDeployDelay() {
return deployDelay;
public double getDefaultDeployDelay() {
return defaultDeploymentConfig.getDeployDelay();
}
public void setDeployDelay(double delay) {
public void setDefaultDeployDelay(double delay) {
delay = MathUtil.max(delay, 0);
if (MathUtil.equals(this.deployDelay, delay))
if (MathUtil.equals(getDefaultDeployDelay(), delay))
return;
this.deployDelay = delay;
defaultDeploymentConfig.setDeployDelay(delay);
fireComponentChangeEvent(ComponentChangeEvent.EVENT_CHANGE);
}

View File

@ -13,6 +13,7 @@ import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
@ -42,6 +43,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private SimulationStepper currentStepper;
private SimulationStatus status;
private String flightConfigurationId;
@Override
@ -53,6 +56,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Set up rocket configuration
Configuration configuration = setupConfiguration(simulationConditions);
flightConfigurationId = configuration.getMotorConfigurationID();
MotorInstanceConfiguration motorConfiguration = setupMotorConfiguration(configuration);
if (motorConfiguration.getMotorIDs().isEmpty()) {
throw new MotorIgnitionException("No motors defined in the simulation.");
@ -371,10 +375,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
RocketComponent c = rci.next();
if (!(c instanceof RecoveryDevice))
continue;
if (((RecoveryDevice) c).getDeployEvent().isActivationEvent(event, c)) {
DeploymentConfiguration deployConfig = ((RecoveryDevice) c).getDeploymentConfiguration(flightConfigurationId);
if ( deployConfig == null ) {
deployConfig = ((RecoveryDevice) c).getDefaultDeploymentConfiguration();
}
if (deployConfig.isActivationEvent(event, c)) {
// Delay event by at least 1ms to allow stage separation to occur first
addEvent(new FlightEvent(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT,
event.getTime() + Math.max(0.001, ((RecoveryDevice) c).getDeployDelay()), c));
event.getTime() + Math.max(0.001, deployConfig.getDeployDelay()), c));
}
}