Implement feature to compute the optimum delay for a simulation.
This commit is contained in:
parent
4565cdcd86
commit
a34d41d6af
@ -430,6 +430,8 @@ simpanel.col.Motors = Motors
|
||||
simpanel.col.Configuration = Configuration
|
||||
simpanel.col.Velocityoffrod = Velocity off rod
|
||||
simpanel.col.Velocityatdeploy = Velocity at deployment
|
||||
simpanel.col.OptimumCoastTime = Optimum delay
|
||||
simpanel.col.OptimumCoastTime.ttip = The time between last motor burnout and maximum possible altitude.
|
||||
simpanel.col.Apogee = Apogee
|
||||
simpanel.col.Maxvelocity = Max. velocity
|
||||
simpanel.col.Maxacceleration = Max. acceleration
|
||||
|
@ -536,6 +536,7 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<databranch name=\"");
|
||||
sb.append(TextUtil.escapeXML(branch.getBranchName()));
|
||||
sb.append("\" ");
|
||||
|
||||
// Kevins version where typekeys are used
|
||||
/*
|
||||
@ -547,7 +548,19 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
}
|
||||
*/
|
||||
|
||||
sb.append("\" types=\"");
|
||||
if (!Double.isNaN(branch.getOptimumAltitude())) {
|
||||
sb.append("optimumAltitude=\"");
|
||||
sb.append(branch.getOptimumAltitude());
|
||||
sb.append("\" ");
|
||||
}
|
||||
|
||||
if (!Double.isNaN(branch.getTimeToOptimumAltitude())) {
|
||||
sb.append("timeToOptimumAltitude=\"");
|
||||
sb.append(branch.getTimeToOptimumAltitude());
|
||||
sb.append("\" ");
|
||||
}
|
||||
|
||||
sb.append("types=\"");
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (i > 0)
|
||||
sb.append(",");
|
||||
@ -590,8 +603,6 @@ public class OpenRocketSaver extends RocketSaver {
|
||||
writeln("</databranch>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TODO: LOW: This is largely duplicated from above! */
|
||||
private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) {
|
||||
int count = 0;
|
||||
|
@ -42,6 +42,22 @@ class FlightDataBranchHandler extends AbstractElementHandler {
|
||||
branch = new FlightDataBranch(name, types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeToOptimumAltitude
|
||||
* @see net.sf.openrocket.simulation.FlightDataBranch#setTimeToOptimumAltitude(double)
|
||||
*/
|
||||
public void setTimeToOptimumAltitude(double timeToOptimumAltitude) {
|
||||
branch.setTimeToOptimumAltitude(timeToOptimumAltitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param optimumAltitude
|
||||
* @see net.sf.openrocket.simulation.FlightDataBranch#setOptimumAltitude(double)
|
||||
*/
|
||||
public void setOptimumAltitude(double optimumAltitude) {
|
||||
branch.setOptimumAltitude(optimumAltitude);
|
||||
}
|
||||
|
||||
// Find the full flight data type given name only
|
||||
// Note: this way of doing it requires that custom expressions always come before flight data in the file,
|
||||
// not the nicest but this is always the case anyway.
|
||||
|
@ -48,6 +48,23 @@ class FlightDataHandler extends AbstractElementHandler {
|
||||
dataHandler = new FlightDataBranchHandler(attributes.get("name"),
|
||||
attributes.get("types"),
|
||||
simHandler, context);
|
||||
|
||||
if (attributes.get("optimumAltitude") != null) {
|
||||
double optimumAltitude = Double.NaN;
|
||||
try {
|
||||
optimumAltitude = Double.parseDouble(attributes.get("optimumAltitude"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
dataHandler.setOptimumAltitude(optimumAltitude);
|
||||
}
|
||||
if (attributes.get("timeToOptimumAltitude") != null) {
|
||||
double timeToOptimumAltitude = Double.NaN;
|
||||
try {
|
||||
timeToOptimumAltitude = Double.parseDouble(attributes.get("timeToOptimumAltitude"));
|
||||
} catch (NumberFormatException ignore) {
|
||||
}
|
||||
dataHandler.setTimeToOptimumAltitude(timeToOptimumAltitude);
|
||||
}
|
||||
return dataHandler;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
|
||||
import net.sf.openrocket.simulation.listeners.system.OptimumCoastListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -120,8 +121,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
Coordinate originVelocity = status.getRocketVelocity();
|
||||
|
||||
try {
|
||||
double maxAlt = Double.NEGATIVE_INFINITY;
|
||||
|
||||
// Start the simulation
|
||||
while (handleEvents()) {
|
||||
|
||||
@ -149,8 +148,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
status.getConfiguration().getRocket(),
|
||||
new Pair<Double, Double>(oldAlt, status.getRocketPosition().z)));
|
||||
|
||||
if (status.getRocketPosition().z > maxAlt) {
|
||||
maxAlt = status.getRocketPosition().z;
|
||||
if (status.getRocketPosition().z > status.getMaxAlt()) {
|
||||
status.setMaxAlt(status.getRocketPosition().z);
|
||||
}
|
||||
|
||||
|
||||
@ -189,7 +188,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
|
||||
// Check for apogee
|
||||
if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) {
|
||||
if (!status.isApogeeReached() && status.getRocketPosition().z < status.getMaxAlt() - 0.01) {
|
||||
status.setMaxAltTime(status.getSimulationTime());
|
||||
addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(),
|
||||
status.getConfiguration().getRocket()));
|
||||
}
|
||||
@ -464,6 +464,11 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
// Mark apogee as reached
|
||||
status.setApogeeReached(true);
|
||||
status.getFlightData().addEvent(event);
|
||||
// This apogee event might be the optimum if recovery has not already happened.
|
||||
if (status.getSimulationConditions().isCalculateExtras() && status.getDeployedRecoveryDevices().size() == 0) {
|
||||
status.getFlightData().setOptimumAltitude(status.getMaxAlt());
|
||||
status.getFlightData().setTimeToOptimumAltitude(status.getMaxAltTime());
|
||||
}
|
||||
break;
|
||||
|
||||
case RECOVERY_DEVICE_DEPLOYMENT:
|
||||
@ -501,6 +506,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
status.setLiftoff(true);
|
||||
status.getDeployedRecoveryDevices().add((RecoveryDevice) c);
|
||||
|
||||
// If we haven't already reached apogee, then we need to compute the actual coast time
|
||||
// to determine the optimum altitude.
|
||||
if (status.getSimulationConditions().isCalculateExtras() && !status.isApogeeReached()) {
|
||||
FlightData coastStatus = computeCoastTime();
|
||||
status.getFlightData().setOptimumAltitude(coastStatus.getMaxAltitude());
|
||||
status.getFlightData().setTimeToOptimumAltitude(coastStatus.getTimeToApogee());
|
||||
}
|
||||
|
||||
this.currentStepper = this.landingStepper;
|
||||
this.status = currentStepper.initialize(status);
|
||||
|
||||
@ -601,5 +614,17 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private FlightData computeCoastTime() {
|
||||
try {
|
||||
SimulationConditions conds = status.getSimulationConditions().clone();
|
||||
conds.getSimulationListenerList().add(OptimumCoastListener.INSTANCE);
|
||||
BasicEventSimulationEngine e = new BasicEventSimulationEngine();
|
||||
|
||||
FlightData d = e.simulate(conds);
|
||||
return d;
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception computing coast time: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,15 @@ public class FlightDataBranch implements Monitorable {
|
||||
private final Map<FlightDataType, Double> maxValues = new HashMap<FlightDataType, Double>();
|
||||
private final Map<FlightDataType, Double> minValues = new HashMap<FlightDataType, Double>();
|
||||
|
||||
|
||||
/**
|
||||
* time for the rocket to reach apogee if the flight had been no recovery deployment
|
||||
*/
|
||||
private double timeToOptimumAltitude = Double.NaN;
|
||||
/**
|
||||
* Altitude the rocket would reach if there had been no recovery deployment.
|
||||
*/
|
||||
private double optimumAltitude = Double.NaN;
|
||||
|
||||
private final ArrayList<FlightEvent> events = new ArrayList<FlightEvent>();
|
||||
|
||||
private Mutable mutable = new Mutable();
|
||||
@ -73,12 +81,12 @@ public class FlightDataBranch implements Monitorable {
|
||||
*/
|
||||
public FlightDataBranch() {
|
||||
branchName = "Empty branch";
|
||||
for (FlightDataType type : FlightDataType.ALL_TYPES){
|
||||
for (FlightDataType type : FlightDataType.ALL_TYPES) {
|
||||
this.setValue(type, Double.NaN);
|
||||
}
|
||||
this.immute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new point into the data branch. The value for all types is set to NaN by default.
|
||||
*
|
||||
@ -115,10 +123,10 @@ public class FlightDataBranch implements Monitorable {
|
||||
}
|
||||
values.put(type, list);
|
||||
minValues.put(type, value);
|
||||
maxValues.put(type, value);
|
||||
maxValues.put(type, value);
|
||||
}
|
||||
|
||||
if (list.size() > 0){
|
||||
if (list.size() > 0) {
|
||||
list.set(list.size() - 1, value);
|
||||
}
|
||||
|
||||
@ -219,6 +227,50 @@ public class FlightDataBranch implements Monitorable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the timeToOptimumAltitude
|
||||
*/
|
||||
public double getTimeToOptimumAltitude() {
|
||||
return timeToOptimumAltitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeToOptimumAltitude the timeToOptimumAltitude to set
|
||||
*/
|
||||
public void setTimeToOptimumAltitude(double timeToOptimumAltitude) {
|
||||
this.timeToOptimumAltitude = timeToOptimumAltitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the optimumAltitude
|
||||
*/
|
||||
public double getOptimumAltitude() {
|
||||
return optimumAltitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param optimumAltitude the optimumAltitude to set
|
||||
*/
|
||||
public void setOptimumAltitude(double optimumAltitude) {
|
||||
this.optimumAltitude = optimumAltitude;
|
||||
}
|
||||
|
||||
public double getOptimumDelay() {
|
||||
|
||||
if (Double.isNaN(timeToOptimumAltitude)) {
|
||||
return Double.NaN;
|
||||
}
|
||||
// TODO - we really want the first burnout of this stage. which
|
||||
// could be computed as the first burnout after the last stage separation event.
|
||||
// however, that's not quite so concise
|
||||
FlightEvent e = getLastEvent(FlightEvent.Type.BURNOUT);
|
||||
if (e != null) {
|
||||
return timeToOptimumAltitude - e.getTime();
|
||||
}
|
||||
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flight event to this branch.
|
||||
*
|
||||
@ -241,6 +293,34 @@ public class FlightDataBranch implements Monitorable {
|
||||
return events.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first event of the given type.
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public FlightEvent getFirstEvent(FlightEvent.Type type) {
|
||||
for (FlightEvent e : events) {
|
||||
if (e.getType() == type) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last event of the given type.
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public FlightEvent getLastEvent(FlightEvent.Type type) {
|
||||
FlightEvent retval = null;
|
||||
for (FlightEvent e : events) {
|
||||
if (e.getType() == type) {
|
||||
retval = e;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this FlightDataBranch immutable. Any calls to the set methods that would
|
||||
|
@ -29,7 +29,7 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
private String motorID = null;
|
||||
|
||||
private Simulation simulation; // The parent simulation
|
||||
|
||||
|
||||
private double launchRodLength = 1;
|
||||
|
||||
/** Launch rod angle >= 0, radians from vertical */
|
||||
@ -45,7 +45,7 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
private WorldCoordinate launchSite = new WorldCoordinate(0, 0, 0);
|
||||
private GeodeticComputationStrategy geodeticComputation = GeodeticComputationStrategy.SPHERICAL;
|
||||
|
||||
|
||||
|
||||
private WindModel windModel;
|
||||
private AtmosphericModel atmosphericModel;
|
||||
private GravityModel gravityModel;
|
||||
@ -53,25 +53,25 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
private AerodynamicCalculator aerodynamicCalculator;
|
||||
private MassCalculator massCalculator;
|
||||
|
||||
|
||||
|
||||
private double timeStep = RK4SimulationStepper.RECOMMENDED_TIME_STEP;
|
||||
private double maximumAngleStep = RK4SimulationStepper.RECOMMENDED_ANGLE_STEP;
|
||||
|
||||
/* Whether to calculate additional data or only primary simulation figures */
|
||||
private boolean calculateExtras = true;
|
||||
|
||||
|
||||
|
||||
private List<SimulationListener> simulationListeners = new ArrayList<SimulationListener>();
|
||||
|
||||
|
||||
|
||||
private int randomSeed = 0;
|
||||
|
||||
private int modID = 0;
|
||||
private int modIDadd = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public AerodynamicCalculator getAerodynamicCalculator() {
|
||||
return aerodynamicCalculator;
|
||||
}
|
||||
@ -253,7 +253,7 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getRandomSeed() {
|
||||
return randomSeed;
|
||||
}
|
||||
@ -267,8 +267,8 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
public void setSimulation(Simulation sim) {
|
||||
this.simulation = sim;
|
||||
}
|
||||
|
||||
public Simulation getSimulation(){
|
||||
|
||||
public Simulation getSimulation() {
|
||||
return this.simulation;
|
||||
}
|
||||
|
||||
@ -291,7 +291,12 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
||||
public SimulationConditions clone() {
|
||||
try {
|
||||
// TODO: HIGH: Deep clone models
|
||||
return (SimulationConditions) super.clone();
|
||||
SimulationConditions clone = (SimulationConditions) super.clone();
|
||||
clone.simulationListeners = new ArrayList<SimulationListener>(this.simulationListeners.size());
|
||||
for (SimulationListener listener : this.simulationListeners) {
|
||||
clone.simulationListeners.add(listener.clone());
|
||||
}
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new BugException(e);
|
||||
}
|
||||
|
@ -27,10 +27,6 @@ import net.sf.openrocket.util.WorldCoordinate;
|
||||
*/
|
||||
public class SimulationStatus implements Monitorable {
|
||||
|
||||
/*
|
||||
* NOTE! All fields must be added to copyFrom() method!!
|
||||
*/
|
||||
|
||||
private SimulationConditions simulationConditions;
|
||||
private Configuration configuration;
|
||||
private MotorInstanceConfiguration motorConfiguration;
|
||||
@ -51,12 +47,12 @@ public class SimulationStatus implements Monitorable {
|
||||
|
||||
// Set of burnt out motors
|
||||
Set<MotorId> motorBurntOut = new HashSet<MotorId>();
|
||||
|
||||
|
||||
|
||||
|
||||
/** Nanosecond time when the simulation was started. */
|
||||
private long simulationStartWallTime = Long.MIN_VALUE;
|
||||
|
||||
|
||||
|
||||
/** Set to true when a motor has ignited. */
|
||||
private boolean motorIgnited = false;
|
||||
|
||||
@ -83,38 +79,40 @@ public class SimulationStatus implements Monitorable {
|
||||
/** Available for special purposes by the listeners. */
|
||||
private final Map<String, Object> extraData = new HashMap<String, Object>();
|
||||
|
||||
|
||||
double maxAlt = Double.NEGATIVE_INFINITY;
|
||||
double maxAltTime = 0;
|
||||
|
||||
private int modID = 0;
|
||||
private int modIDadd = 0;
|
||||
|
||||
public SimulationStatus( Configuration configuration,
|
||||
public SimulationStatus(Configuration configuration,
|
||||
MotorInstanceConfiguration motorConfiguration,
|
||||
SimulationConditions simulationConditions ) {
|
||||
SimulationConditions simulationConditions) {
|
||||
|
||||
this.simulationConditions = simulationConditions;
|
||||
this.configuration = configuration;
|
||||
this.motorConfiguration = motorConfiguration;
|
||||
|
||||
|
||||
this.time = 0;
|
||||
this.previousTimeStep = this.simulationConditions.getTimeStep();
|
||||
this.position = Coordinate.NUL;
|
||||
this.velocity = Coordinate.NUL;
|
||||
this.worldPosition = this.simulationConditions.getLaunchSite();
|
||||
|
||||
|
||||
// Initialize to roll angle with least stability w.r.t. the wind
|
||||
Quaternion o;
|
||||
FlightConditions cond = new FlightConditions(this.configuration);
|
||||
this.simulationConditions.getAerodynamicCalculator().getWorstCP(this.configuration, cond, null);
|
||||
double angle = -cond.getTheta() - this.simulationConditions.getLaunchRodDirection();
|
||||
o = Quaternion.rotation(new Coordinate(0, 0, angle));
|
||||
|
||||
|
||||
// Launch rod angle and direction
|
||||
o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, this.simulationConditions.getLaunchRodAngle(), 0)));
|
||||
o = o.multiplyLeft(Quaternion.rotation(new Coordinate(0, 0, this.simulationConditions.getLaunchRodDirection())));
|
||||
|
||||
|
||||
this.orientation = o;
|
||||
this.rotationVelocity = Coordinate.NUL;
|
||||
|
||||
|
||||
/*
|
||||
* Calculate the effective launch rod length taking into account launch lugs.
|
||||
* If no lugs are found, assume a tower launcher of full length.
|
||||
@ -140,16 +138,16 @@ public class SimulationStatus implements Monitorable {
|
||||
}
|
||||
}
|
||||
this.effectiveLaunchRodLength = length;
|
||||
|
||||
|
||||
this.simulationStartWallTime = System.nanoTime();
|
||||
|
||||
|
||||
this.motorIgnited = false;
|
||||
this.liftoff = false;
|
||||
this.launchRodCleared = false;
|
||||
this.apogeeReached = false;
|
||||
|
||||
|
||||
this.warnings = new WarningSet();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,7 +161,7 @@ public class SimulationStatus implements Monitorable {
|
||||
*
|
||||
* @param orig the object from which to copy
|
||||
*/
|
||||
public SimulationStatus( SimulationStatus orig ) {
|
||||
public SimulationStatus(SimulationStatus orig) {
|
||||
this.simulationConditions = orig.simulationConditions.clone();
|
||||
this.configuration = orig.configuration.clone();
|
||||
this.motorConfiguration = orig.motorConfiguration.clone();
|
||||
@ -292,11 +290,11 @@ public class SimulationStatus implements Monitorable {
|
||||
}
|
||||
|
||||
|
||||
public boolean addBurntOutMotor( MotorId motor ) {
|
||||
public boolean addBurntOutMotor(MotorId motor) {
|
||||
return motorBurntOut.add(motor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Quaternion getRocketOrientationQuaternion() {
|
||||
return orientation;
|
||||
}
|
||||
@ -384,7 +382,7 @@ public class SimulationStatus implements Monitorable {
|
||||
}
|
||||
|
||||
|
||||
public void setTumbling( boolean tumbling ) {
|
||||
public void setTumbling(boolean tumbling) {
|
||||
this.tumbling = tumbling;
|
||||
this.modID++;
|
||||
}
|
||||
@ -393,6 +391,24 @@ public class SimulationStatus implements Monitorable {
|
||||
return tumbling;
|
||||
}
|
||||
|
||||
public double getMaxAlt() {
|
||||
return maxAlt;
|
||||
}
|
||||
|
||||
public void setMaxAlt(double maxAlt) {
|
||||
this.maxAlt = maxAlt;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
public double getMaxAltTime() {
|
||||
return maxAltTime;
|
||||
}
|
||||
|
||||
public void setMaxAltTime(double maxAltTime) {
|
||||
this.maxAltTime = maxAltTime;
|
||||
this.modID++;
|
||||
}
|
||||
|
||||
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
||||
return deployedRecoveryDevices;
|
||||
}
|
||||
@ -477,5 +493,5 @@ public class SimulationStatus implements Monitorable {
|
||||
eventQueue.getModID() + warnings.getModID());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
||||
* Return an array of any flight data types this listener creates.
|
||||
*/
|
||||
@Override
|
||||
public FlightDataType[] getFlightDataTypes(){
|
||||
public FlightDataType[] getFlightDataTypes() {
|
||||
return new FlightDataType[] {};
|
||||
}
|
||||
|
||||
@ -183,4 +183,9 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractSimulationListener clone() throws CloneNotSupportedException {
|
||||
return (AbstractSimulationListener) super.clone();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,9 +4,13 @@ import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
|
||||
|
||||
|
||||
public interface SimulationListener {
|
||||
/**
|
||||
* Listen to simulation events and possibly take action.
|
||||
*
|
||||
* If the implementation maintains any state, it should be properly cloned.
|
||||
*
|
||||
*/
|
||||
public interface SimulationListener extends Cloneable {
|
||||
|
||||
/**
|
||||
* Get the name of this simulation listener. Ideally this should be localized, as
|
||||
@ -83,5 +87,5 @@ public interface SimulationListener {
|
||||
*/
|
||||
public FlightDataType[] getFlightDataTypes();
|
||||
|
||||
|
||||
public SimulationListener clone() throws CloneNotSupportedException;
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package net.sf.openrocket.simulation.listeners.system;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
/**
|
||||
* Simulation listener which ignores recovery deployment events and ends the simulation
|
||||
* when apogee is reached.
|
||||
*
|
||||
* @author kevin
|
||||
*
|
||||
*/
|
||||
public class OptimumCoastListener extends AbstractSimulationListener {
|
||||
|
||||
public static final OptimumCoastListener INSTANCE = new OptimumCoastListener();
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) {
|
||||
if (event.getType() == FlightEvent.Type.APOGEE) {
|
||||
status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice) throws SimulationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSystemListener() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -6,6 +6,7 @@ import javax.swing.table.TableColumnModel;
|
||||
|
||||
public abstract class Column {
|
||||
private final String name;
|
||||
private final String toolTip;
|
||||
|
||||
/**
|
||||
* Create a new column with specified name. Additionally, the {@link #getValueAt(int)}
|
||||
@ -15,6 +16,17 @@ public abstract class Column {
|
||||
*/
|
||||
public Column(String name) {
|
||||
this.name = name;
|
||||
this.toolTip = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new column with specified name and toolTip.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public Column(String name, String toolTip ) {
|
||||
this.name = name;
|
||||
this.toolTip = toolTip;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,5 +99,9 @@ public abstract class Column {
|
||||
public Comparator getComparator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getToolTip() {
|
||||
return toolTip;
|
||||
}
|
||||
|
||||
}
|
||||
|
28
swing/src/net/sf/openrocket/gui/adaptors/ColumnTable.java
Normal file
28
swing/src/net/sf/openrocket/gui/adaptors/ColumnTable.java
Normal file
@ -0,0 +1,28 @@
|
||||
package net.sf.openrocket.gui.adaptors;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.table.JTableHeader;
|
||||
|
||||
public class ColumnTable extends JTable {
|
||||
|
||||
public ColumnTable( ColumnTableModel model ) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JTableHeader createDefaultTableHeader() {
|
||||
return new JTableHeader( columnModel ) {
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
String tip = null;
|
||||
java.awt.Point p = e.getPoint();
|
||||
int index = columnModel.getColumnIndexAtX(p.x);
|
||||
int realIndex = columnModel.getColumn(index).getModelIndex();
|
||||
tip = ((ColumnTableModel) getModel()).getColumn(realIndex).getToolTip();
|
||||
return tip;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,11 @@ public abstract class ValueColumn extends Column {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public ValueColumn( String name, String toolTip, UnitGroup unit ) {
|
||||
super( name, toolTip );
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
Double d = valueAt(row);
|
||||
|
@ -43,6 +43,7 @@ import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.Warning;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.gui.adaptors.Column;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||
import net.sf.openrocket.gui.adaptors.FlightConfigurationModel;
|
||||
@ -243,7 +244,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
}
|
||||
};
|
||||
|
||||
table = new JTable(cpTableModel);
|
||||
table = new ColumnTable(cpTableModel);
|
||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.setSelectionBackground(Color.LIGHT_GRAY);
|
||||
table.setSelectionForeground(Color.BLACK);
|
||||
|
@ -40,6 +40,7 @@ import javax.swing.table.TableRowSorter;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.gui.adaptors.Column;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||
import net.sf.openrocket.gui.components.SelectableLabel;
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
@ -266,7 +267,7 @@ public class DebugLogDialog extends JDialog {
|
||||
}
|
||||
};
|
||||
|
||||
table = new JTable(model);
|
||||
table = new ColumnTable(model);
|
||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
table.setSelectionBackground(Color.LIGHT_GRAY);
|
||||
table.setSelectionForeground(Color.BLACK);
|
||||
|
@ -23,6 +23,7 @@ import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.openrocket.database.Database;
|
||||
import net.sf.openrocket.database.Databases;
|
||||
import net.sf.openrocket.gui.adaptors.Column;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel;
|
||||
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
||||
@ -108,7 +109,7 @@ public class MaterialEditPanel extends JPanel {
|
||||
}
|
||||
};
|
||||
|
||||
table = new JTable(model);
|
||||
table = new ColumnTable(model);
|
||||
model.setColumnWidths(table.getColumnModel());
|
||||
table.setAutoCreateRowSorter(true);
|
||||
table.setDefaultRenderer(Object.class, new MaterialCellRenderer());
|
||||
|
@ -35,6 +35,7 @@ import net.sf.openrocket.document.events.DocumentChangeListener;
|
||||
import net.sf.openrocket.document.events.SimulationChangeEvent;
|
||||
import net.sf.openrocket.formatting.RocketDescriptor;
|
||||
import net.sf.openrocket.gui.adaptors.Column;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||
import net.sf.openrocket.gui.adaptors.ColumnTableRowSorter;
|
||||
import net.sf.openrocket.gui.adaptors.ValueColumn;
|
||||
@ -48,6 +49,7 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
|
||||
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||
import net.sf.openrocket.simulation.FlightData;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.startup.Preferences;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
@ -59,37 +61,37 @@ import org.slf4j.LoggerFactory;
|
||||
public class SimulationPanel extends JPanel {
|
||||
private static final Logger log = LoggerFactory.getLogger(SimulationPanel.class);
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
|
||||
|
||||
|
||||
private static final Color WARNING_COLOR = Color.RED;
|
||||
private static final String WARNING_TEXT = "\uFF01"; // Fullwidth exclamation mark
|
||||
|
||||
|
||||
private static final Color OK_COLOR = new Color(60, 150, 0);
|
||||
private static final String OK_TEXT = "\u2714"; // Heavy check mark
|
||||
|
||||
|
||||
|
||||
|
||||
private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class);
|
||||
|
||||
|
||||
|
||||
|
||||
private final OpenRocketDocument document;
|
||||
|
||||
|
||||
private final ColumnTableModel simulationTableModel;
|
||||
private final JTable simulationTable;
|
||||
|
||||
|
||||
private final JButton editButton;
|
||||
private final JButton runButton;
|
||||
private final JButton deleteButton;
|
||||
private final JButton plotButton;
|
||||
|
||||
|
||||
public SimulationPanel(OpenRocketDocument doc) {
|
||||
super(new MigLayout("fill", "[grow][][][][][][grow]"));
|
||||
|
||||
|
||||
this.document = doc;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////// The simulation action buttons
|
||||
|
||||
|
||||
//// New simulation button
|
||||
{
|
||||
JButton button = new JButton(trans.get("simpanel.but.newsimulation"));
|
||||
@ -100,19 +102,19 @@ public class SimulationPanel extends JPanel {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Simulation sim = new Simulation(document.getRocket());
|
||||
sim.setName(document.getNextSimulationName());
|
||||
|
||||
|
||||
int n = document.getSimulationCount();
|
||||
document.addSimulation(sim);
|
||||
simulationTableModel.fireTableDataChanged();
|
||||
simulationTable.clearSelection();
|
||||
simulationTable.addRowSelectionInterval(n, n);
|
||||
|
||||
|
||||
openDialog(false, sim);
|
||||
}
|
||||
});
|
||||
this.add(button, "skip 1, gapright para");
|
||||
}
|
||||
|
||||
|
||||
//// Edit simulation button
|
||||
editButton = new JButton(trans.get("simpanel.but.editsimulation"));
|
||||
//// Edit the selected simulation
|
||||
@ -133,7 +135,7 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
this.add(editButton, "gapright para");
|
||||
|
||||
|
||||
//// Run simulations
|
||||
runButton = new JButton(trans.get("simpanel.but.runsimulations"));
|
||||
//// Re-run the selected simulations
|
||||
@ -150,7 +152,7 @@ public class SimulationPanel extends JPanel {
|
||||
selection[i] = simulationTable.convertRowIndexToModel(selection[i]);
|
||||
sims[i] = document.getSimulation(selection[i]);
|
||||
}
|
||||
|
||||
|
||||
long t = System.currentTimeMillis();
|
||||
new SimulationRunDialog(SwingUtilities.getWindowAncestor(
|
||||
SimulationPanel.this), document, sims).setVisible(true);
|
||||
@ -159,7 +161,7 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
this.add(runButton, "gapright para");
|
||||
|
||||
|
||||
//// Delete simulations button
|
||||
deleteButton = new JButton(trans.get("simpanel.but.deletesimulations"));
|
||||
//// Delete the selected simulations
|
||||
@ -174,34 +176,34 @@ public class SimulationPanel extends JPanel {
|
||||
// Verify deletion
|
||||
boolean verify = Application.getPreferences().getBoolean(Preferences.CONFIRM_DELETE_SIMULATION, true);
|
||||
if (verify) {
|
||||
|
||||
|
||||
JPanel panel = new JPanel(new MigLayout());
|
||||
//// Do not ask me again
|
||||
JCheckBox dontAsk = new JCheckBox(trans.get("simpanel.checkbox.donotask"));
|
||||
panel.add(dontAsk, "wrap");
|
||||
//// You can change the default operation in the preferences.
|
||||
panel.add(new StyledLabel(trans.get("simpanel.lbl.defpref"), -2));
|
||||
|
||||
|
||||
int ret = JOptionPane.showConfirmDialog(SimulationPanel.this,
|
||||
new Object[] {
|
||||
//// Delete the selected simulations?
|
||||
trans.get("simpanel.dlg.lbl.DeleteSim1"),
|
||||
//// <html><i>This operation cannot be undone.</i>
|
||||
trans.get("simpanel.dlg.lbl.DeleteSim2"),
|
||||
"",
|
||||
panel },
|
||||
//// Delete the selected simulations?
|
||||
trans.get("simpanel.dlg.lbl.DeleteSim1"),
|
||||
//// <html><i>This operation cannot be undone.</i>
|
||||
trans.get("simpanel.dlg.lbl.DeleteSim2"),
|
||||
"",
|
||||
panel },
|
||||
//// Delete simulations
|
||||
trans.get("simpanel.dlg.lbl.DeleteSim3"),
|
||||
JOptionPane.OK_CANCEL_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
if (ret != JOptionPane.OK_OPTION)
|
||||
return;
|
||||
|
||||
|
||||
if (dontAsk.isSelected()) {
|
||||
Application.getPreferences().putBoolean(Preferences.CONFIRM_DELETE_SIMULATION, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delete simulations
|
||||
for (int i = 0; i < selection.length; i++) {
|
||||
selection[i] = simulationTable.convertRowIndexToModel(selection[i]);
|
||||
@ -214,7 +216,7 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
});
|
||||
this.add(deleteButton, "gapright para");
|
||||
|
||||
|
||||
//// Plot / export button
|
||||
plotButton = new JButton(trans.get("simpanel.but.plotexport"));
|
||||
// button = new JButton("Plot flight");
|
||||
@ -228,58 +230,58 @@ public class SimulationPanel extends JPanel {
|
||||
selected = simulationTable.convertRowIndexToModel(selected);
|
||||
simulationTable.clearSelection();
|
||||
simulationTable.addRowSelectionInterval(selected, selected);
|
||||
|
||||
|
||||
|
||||
|
||||
Simulation sim = document.getSimulations().get(selected);
|
||||
|
||||
|
||||
if (!sim.hasSimulationData()) {
|
||||
new SimulationRunDialog(SwingUtilities.getWindowAncestor(
|
||||
SimulationPanel.this), document, sim).setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
fireMaintainSelection();
|
||||
|
||||
|
||||
openDialog(true, sim);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
this.add(plotButton, "wrap para");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////// The simulation table
|
||||
|
||||
|
||||
simulationTableModel = new ColumnTableModel(
|
||||
|
||||
|
||||
//// Status and warning column
|
||||
new Column("") {
|
||||
private JLabel label = null;
|
||||
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
// Initialize the label
|
||||
if (label == null) {
|
||||
label = new StyledLabel(2f);
|
||||
label.setIconTextGap(1);
|
||||
// label.setFont(label.getFont().deriveFont(Font.BOLD));
|
||||
}
|
||||
|
||||
|
||||
// Set simulation status icon
|
||||
Simulation.Status status = document.getSimulation(row).getStatus();
|
||||
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
|
||||
|
||||
|
||||
|
||||
|
||||
// Set warning marker
|
||||
if (status == Simulation.Status.NOT_SIMULATED ||
|
||||
status == Simulation.Status.EXTERNAL) {
|
||||
|
||||
|
||||
label.setText("");
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
WarningSet w = document.getSimulation(row).getSimulatedWarnings();
|
||||
if (w == null) {
|
||||
label.setText("");
|
||||
@ -291,21 +293,21 @@ public class SimulationPanel extends JPanel {
|
||||
label.setText(WARNING_TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getExactWidth() {
|
||||
return 36;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass() {
|
||||
return JLabel.class;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Simulation name
|
||||
//// Name
|
||||
new Column(trans.get("simpanel.col.Name")) {
|
||||
@ -315,18 +317,18 @@ public class SimulationPanel extends JPanel {
|
||||
return null;
|
||||
return document.getSimulation(row).getName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getDefaultWidth() {
|
||||
return 125;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Comparator getComparator() {
|
||||
return new AlphanumComparator();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Simulation configuration
|
||||
new Column(trans.get("simpanel.col.Configuration")) {
|
||||
@Override
|
||||
@ -336,144 +338,165 @@ public class SimulationPanel extends JPanel {
|
||||
Configuration c = document.getSimulation(row).getConfiguration();
|
||||
return descriptor.format(c.getRocket(), c.getFlightConfigurationID());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getDefaultWidth() {
|
||||
return 125;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Launch rod velocity
|
||||
new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getLaunchRodVelocity();
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
//// Apogee
|
||||
new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getMaxAltitude();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Velocity at deployment
|
||||
new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getDeploymentVelocity();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Deployment Time from Apogee
|
||||
new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
|
||||
trans.get("simpanel.col.OptimumCoastTime.ttip"),
|
||||
UnitGroup.UNITS_SHORT_TIME) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null || data.getBranchCount() == 0)
|
||||
return null;
|
||||
|
||||
double val = data.getBranch(0).getOptimumDelay();
|
||||
if ( Double.isNaN(val) ) {
|
||||
return null;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
},
|
||||
|
||||
//// Maximum velocity
|
||||
new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getMaxVelocity();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Maximum acceleration
|
||||
new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getMaxAcceleration();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Time to apogee
|
||||
new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getTimeToApogee();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Flight time
|
||||
new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getFlightTime();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
//// Ground hit velocity
|
||||
new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) {
|
||||
@Override
|
||||
public Double valueAt(int row) {
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return null;
|
||||
|
||||
|
||||
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
||||
return data.getGroundHitVelocity();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
) {
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return document.getSimulationCount();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return document.getSimulationCount();
|
||||
}
|
||||
};
|
||||
|
||||
// Override processKeyBinding so that the JTable does not catch
|
||||
// key bindings used in menu accelerators
|
||||
simulationTable = new JTable(simulationTableModel) {
|
||||
simulationTable = new ColumnTable(simulationTableModel) {
|
||||
@Override
|
||||
protected boolean processKeyBinding(KeyStroke ks,
|
||||
KeyEvent e,
|
||||
@ -487,8 +510,8 @@ public class SimulationPanel extends JPanel {
|
||||
simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||
simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
|
||||
simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
|
||||
|
||||
|
||||
|
||||
|
||||
// Mouse listener to act on double-clicks
|
||||
simulationTable.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@ -499,15 +522,15 @@ public class SimulationPanel extends JPanel {
|
||||
return;
|
||||
}
|
||||
int selected = simulationTable.convertRowIndexToModel(selectedRow);
|
||||
|
||||
|
||||
int column = simulationTable.columnAtPoint(e.getPoint());
|
||||
if (column == 0) {
|
||||
SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected));
|
||||
} else {
|
||||
|
||||
|
||||
simulationTable.clearSelection();
|
||||
simulationTable.addRowSelectionInterval(selectedRow, selectedRow);
|
||||
|
||||
|
||||
openDialog(document.getSimulations().get(selected));
|
||||
}
|
||||
} else {
|
||||
@ -515,7 +538,7 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.addDocumentChangeListener(new DocumentChangeListener() {
|
||||
@Override
|
||||
public void documentChanged(DocumentChangeEvent event) {
|
||||
@ -524,10 +547,10 @@ public class SimulationPanel extends JPanel {
|
||||
simulationTableModel.fireTableDataChanged();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Fire table change event when the rocket changes
|
||||
document.getRocket().addComponentChangeListener(new ComponentChangeListener() {
|
||||
@Override
|
||||
@ -535,14 +558,14 @@ public class SimulationPanel extends JPanel {
|
||||
fireMaintainSelection();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
JScrollPane scrollpane = new JScrollPane(simulationTable);
|
||||
this.add(scrollpane, "spanx, grow, wrap rel");
|
||||
|
||||
|
||||
updateButtonStates();
|
||||
}
|
||||
|
||||
|
||||
private void updateButtonStates() {
|
||||
int[] selection = simulationTable.getSelectedRows();
|
||||
if (selection.length == 0) {
|
||||
@ -560,13 +583,13 @@ public class SimulationPanel extends JPanel {
|
||||
runButton.setEnabled(true);
|
||||
deleteButton.setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ListSelectionModel getSimulationListSelectionModel() {
|
||||
return simulationTable.getSelectionModel();
|
||||
}
|
||||
|
||||
|
||||
private void openDialog(boolean plotMode, final Simulation... sims) {
|
||||
SimulationEditDialog d = new SimulationEditDialog(SwingUtilities.getWindowAncestor(this), document, sims);
|
||||
if (plotMode) {
|
||||
@ -575,7 +598,7 @@ public class SimulationPanel extends JPanel {
|
||||
d.setVisible(true);
|
||||
fireMaintainSelection();
|
||||
}
|
||||
|
||||
|
||||
private void openDialog(final Simulation sim) {
|
||||
boolean plotMode = false;
|
||||
if (sim.hasSimulationData() && (sim.getStatus() == Status.UPTODATE || sim.getStatus() == Status.EXTERNAL)) {
|
||||
@ -583,7 +606,7 @@ public class SimulationPanel extends JPanel {
|
||||
}
|
||||
openDialog(plotMode, sim);
|
||||
}
|
||||
|
||||
|
||||
private void fireMaintainSelection() {
|
||||
int[] selection = simulationTable.getSelectedRows();
|
||||
simulationTableModel.fireTableDataChanged();
|
||||
@ -593,24 +616,24 @@ public class SimulationPanel extends JPanel {
|
||||
simulationTable.addRowSelectionInterval(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum SimulationTableColumns {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class JLabelRenderer extends DefaultTableCellRenderer {
|
||||
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table,
|
||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
||||
int column) {
|
||||
|
||||
|
||||
if (row < 0 || row >= document.getSimulationCount())
|
||||
return super.getTableCellRendererComponent(table, value,
|
||||
isSelected, hasFocus, row, column);
|
||||
|
||||
row = table.getRowSorter().convertRowIndexToModel(row);
|
||||
|
||||
|
||||
// A JLabel is self-contained and has set its own tool tip
|
||||
if (value instanceof JLabel) {
|
||||
JLabel label = (JLabel) value;
|
||||
@ -619,66 +642,66 @@ public class SimulationPanel extends JPanel {
|
||||
else
|
||||
label.setBackground(table.getBackground());
|
||||
label.setOpaque(true);
|
||||
|
||||
|
||||
label.setToolTipText(getSimulationToolTip(document.getSimulation(row)));
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
Component component = super.getTableCellRendererComponent(table, value,
|
||||
isSelected, hasFocus, row, column);
|
||||
|
||||
|
||||
if (component instanceof JComponent) {
|
||||
((JComponent) component).setToolTipText(getSimulationToolTip(
|
||||
document.getSimulation(row)));
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
private String getSimulationToolTip(Simulation sim) {
|
||||
String tip;
|
||||
FlightData data = sim.getSimulatedData();
|
||||
|
||||
|
||||
tip = "<html><b>" + sim.getName() + "</b><br>";
|
||||
switch (sim.getStatus()) {
|
||||
case UPTODATE:
|
||||
tip += trans.get("simpanel.ttip.uptodate") + "<br>";
|
||||
break;
|
||||
|
||||
|
||||
case LOADED:
|
||||
tip += trans.get("simpanel.ttip.loaded") + "<br>";
|
||||
break;
|
||||
|
||||
|
||||
case OUTDATED:
|
||||
tip += trans.get("simpanel.ttip.outdated") + "<br>";
|
||||
break;
|
||||
|
||||
|
||||
case EXTERNAL:
|
||||
tip += trans.get("simpanel.ttip.external") + "<br>";
|
||||
return tip;
|
||||
|
||||
|
||||
case NOT_SIMULATED:
|
||||
tip += trans.get("simpanel.ttip.notSimulated");
|
||||
return tip;
|
||||
}
|
||||
|
||||
|
||||
if (data == null) {
|
||||
tip += trans.get("simpanel.ttip.noData");
|
||||
return tip;
|
||||
}
|
||||
WarningSet warnings = data.getWarningSet();
|
||||
|
||||
|
||||
if (warnings.isEmpty()) {
|
||||
tip += trans.get("simpanel.ttip.noWarnings");
|
||||
return tip;
|
||||
}
|
||||
|
||||
|
||||
tip += trans.get("simpanel.ttip.warnings");
|
||||
for (Warning w : warnings) {
|
||||
tip += "<br>" + w.toString();
|
||||
}
|
||||
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ public class DesignReport {
|
||||
private static final String SIZE = "Size";
|
||||
private static final String ALTITUDE = "Altitude";
|
||||
private static final String FLIGHT_TIME = "Flight Time";
|
||||
private static final String OPTIMUM_DELAY = "Optimum Delay";
|
||||
private static final String TIME_TO_APOGEE = "Time to Apogee";
|
||||
private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
|
||||
private static final String MAX_VELOCITY = "Max Velocity";
|
||||
@ -468,6 +469,9 @@ public class DesignReport {
|
||||
labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
|
||||
labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2));
|
||||
|
||||
labelTable.addCell(ITextHelper.createCell(OPTIMUM_DELAY, 2, 2));
|
||||
labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getBranch(0).getOptimumDelay()), 2, 2));
|
||||
|
||||
labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
|
||||
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2));
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user