SafetyMutex and rocket optimization updates
This commit is contained in:
parent
833e2f80cf
commit
a63f0b4bae
@ -10,7 +10,6 @@
|
||||
</accessrules>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/junit-4.7.jar"/>
|
||||
<classpathentry kind="lib" path="lib/jfreechart-1.0.13.jar" sourcepath="/home/sampo/Projects/lib/jfreechart-1.0.13/source"/>
|
||||
<classpathentry kind="lib" path="lib/jcommon-1.0.16.jar">
|
||||
<accessrules>
|
||||
@ -18,5 +17,10 @@
|
||||
</accessrules>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="lib/miglayout15-swing.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/hamcrest-core-1.3.0RC1.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/hamcrest-library-1.3.0RC1.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/jmock-2.6.0-RC2.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/jmock-junit4-2.6.0-RC2.jar"/>
|
||||
<classpathentry kind="lib" path="lib-test/junit-dep-4.8.2.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
||||
2010-10-25 Doug Pedrick
|
||||
|
||||
* [BUG] Take launch lug radial angle into account when loading rkt file
|
||||
|
||||
2010-10-24 Sampo Niskane
|
||||
|
||||
* Added SafetyMutex and took into use in Simulation
|
||||
|
||||
2010-10-18 Sampo Niskanen
|
||||
|
||||
* Ignore Sun JRE bug in D3D
|
||||
|
||||
2010-10-09 Sampo Niskanen
|
||||
|
||||
* [BUG] Fixed conversion to freeform fin set
|
||||
|
@ -36,8 +36,11 @@
|
||||
<pathelement location="${build-test.dir}"/>
|
||||
<pathelement location="${classes.dir}"/>
|
||||
<pathelement location="${src-test.dir}"/>
|
||||
<!-- <pathelement location="${ant.library.dir}/junit4.jar"/> -->
|
||||
<pathelement location="lib-test/junit-4.7.jar"/>
|
||||
<pathelement location="lib-test/junit-dep-4.8.2.jar"/>
|
||||
<pathelement location="lib-test/hamcrest-core-1.3.0RC1.jar"/>
|
||||
<pathelement location="lib-test/hamcrest-library-1.3.0RC1.jar"/>
|
||||
<pathelement location="lib-test/jmock-2.6.0-RC2.jar"/>
|
||||
<pathelement location="lib-test/jmock-junit4-2.6.0-RC2.jar"/>
|
||||
</path>
|
||||
|
||||
|
||||
|
@ -25,6 +25,14 @@ openrocket.log.tracelevel
|
||||
Debugging options
|
||||
-----------------
|
||||
|
||||
openrocket.debug
|
||||
Turns on various options useful for debugging purposes. The parameters defined are:
|
||||
openrocket.log.stdout=VBOSE
|
||||
openrocket.log.tracelevel=VBOSE
|
||||
openrocket.debug.menu=true
|
||||
openrocket.debug.motordigest=true
|
||||
|
||||
|
||||
openrocket.debug.menu
|
||||
If defined the "Debug" menu will be displayed in the main application window.
|
||||
|
||||
|
BIN
lib-test/hamcrest-core-1.3.0RC1.jar
Normal file
BIN
lib-test/hamcrest-core-1.3.0RC1.jar
Normal file
Binary file not shown.
BIN
lib-test/hamcrest-library-1.3.0RC1.jar
Normal file
BIN
lib-test/hamcrest-library-1.3.0RC1.jar
Normal file
Binary file not shown.
BIN
lib-test/jmock-2.6.0-RC2.jar
Normal file
BIN
lib-test/jmock-2.6.0-RC2.jar
Normal file
Binary file not shown.
BIN
lib-test/jmock-junit4-2.6.0-RC2.jar
Normal file
BIN
lib-test/jmock-junit4-2.6.0-RC2.jar
Normal file
Binary file not shown.
Binary file not shown.
@ -27,8 +27,16 @@ import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.ChangeSource;
|
||||
import net.sf.openrocket.util.SafetyMutex;
|
||||
|
||||
|
||||
/**
|
||||
* A class defining a simulation, its conditions and simulated data.
|
||||
* <p>
|
||||
* This class is not thread-safe and enforces single-threaded access with a
|
||||
* SafetyMutex.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class Simulation implements ChangeSource, Cloneable {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
@ -49,6 +57,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
NOT_SIMULATED
|
||||
}
|
||||
|
||||
private final SafetyMutex mutex = new SafetyMutex();
|
||||
|
||||
private final Rocket rocket;
|
||||
|
||||
@ -146,6 +155,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the rocket.
|
||||
*/
|
||||
public Rocket getRocket() {
|
||||
mutex.verify();
|
||||
return rocket;
|
||||
}
|
||||
|
||||
@ -157,6 +167,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return a newly created Configuration of the launch conditions.
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
mutex.verify();
|
||||
Configuration c = new Configuration(rocket);
|
||||
c.setMotorConfigurationID(conditions.getMotorConfigurationID());
|
||||
c.setAllStages();
|
||||
@ -171,6 +182,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the simulation conditions.
|
||||
*/
|
||||
public GUISimulationConditions getConditions() {
|
||||
mutex.verify();
|
||||
return conditions;
|
||||
}
|
||||
|
||||
@ -182,6 +194,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the actual list of simulation listeners.
|
||||
*/
|
||||
public List<String> getSimulationListeners() {
|
||||
mutex.verify();
|
||||
return simulationListeners;
|
||||
}
|
||||
|
||||
@ -192,6 +205,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the name for the simulation.
|
||||
*/
|
||||
public String getName() {
|
||||
mutex.verify();
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -202,15 +216,20 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @param name the name of the simulation.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
if (this.name.equals(name))
|
||||
return;
|
||||
|
||||
if (name == null)
|
||||
this.name = "";
|
||||
else
|
||||
this.name = name;
|
||||
|
||||
fireChangeEvent();
|
||||
mutex.lock("setName");
|
||||
try {
|
||||
if (this.name.equals(name))
|
||||
return;
|
||||
|
||||
if (name == null)
|
||||
this.name = "";
|
||||
else
|
||||
this.name = name;
|
||||
|
||||
fireChangeEvent();
|
||||
} finally {
|
||||
mutex.unlock("setName");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -222,6 +241,8 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @see Status
|
||||
*/
|
||||
public Status getStatus() {
|
||||
mutex.verify();
|
||||
|
||||
if (status == Status.UPTODATE || status == Status.LOADED) {
|
||||
if (rocket.getFunctionalModID() != simulatedRocketID ||
|
||||
!conditions.equals(simulatedConditions))
|
||||
@ -236,54 +257,59 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
|
||||
public void simulate(SimulationListener... additionalListeners)
|
||||
throws SimulationException {
|
||||
|
||||
if (this.status == Status.EXTERNAL) {
|
||||
throw new SimulationException("Cannot simulate imported simulation.");
|
||||
}
|
||||
|
||||
SimulationEngine simulator;
|
||||
|
||||
mutex.lock("simulate");
|
||||
try {
|
||||
simulator = simulationEngineClass.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException("Cannot instantiate simulator.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot access simulator instance?! BUG!", e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalStateException("Simulator null", e);
|
||||
}
|
||||
|
||||
SimulationConditions simulationConditions = conditions.toSimulationConditions();
|
||||
for (SimulationListener l : additionalListeners) {
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
}
|
||||
|
||||
for (String className : simulationListeners) {
|
||||
SimulationListener l = null;
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
l = (SimulationListener) c.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new SimulationListenerException("Could not instantiate listener of " +
|
||||
"class: " + className, e);
|
||||
|
||||
if (this.status == Status.EXTERNAL) {
|
||||
throw new SimulationException("Cannot simulate imported simulation.");
|
||||
}
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
|
||||
SimulationEngine simulator;
|
||||
|
||||
try {
|
||||
simulator = simulationEngineClass.newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException("Cannot instantiate simulator.", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Cannot access simulator instance?! BUG!", e);
|
||||
} catch (NullPointerException e) {
|
||||
throw new IllegalStateException("Simulator null", e);
|
||||
}
|
||||
|
||||
SimulationConditions simulationConditions = conditions.toSimulationConditions();
|
||||
for (SimulationListener l : additionalListeners) {
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
}
|
||||
|
||||
for (String className : simulationListeners) {
|
||||
SimulationListener l = null;
|
||||
try {
|
||||
Class<?> c = Class.forName(className);
|
||||
l = (SimulationListener) c.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new SimulationListenerException("Could not instantiate listener of " +
|
||||
"class: " + className, e);
|
||||
}
|
||||
simulationConditions.getSimulationListenerList().add(l);
|
||||
}
|
||||
|
||||
long t1, t2;
|
||||
log.debug("Simulation: calling simulator");
|
||||
t1 = System.currentTimeMillis();
|
||||
simulatedData = simulator.simulate(simulationConditions);
|
||||
t2 = System.currentTimeMillis();
|
||||
log.debug("Simulation: returning from simulator, simulation took " + (t2 - t1) + "ms");
|
||||
|
||||
// Set simulated info after simulation, will not be set in case of exception
|
||||
simulatedConditions = conditions.clone();
|
||||
simulatedMotors = getConfiguration().getMotorConfigurationDescription();
|
||||
simulatedRocketID = rocket.getFunctionalModID();
|
||||
|
||||
status = Status.UPTODATE;
|
||||
fireChangeEvent();
|
||||
} finally {
|
||||
mutex.unlock("simulate");
|
||||
}
|
||||
|
||||
long t1, t2;
|
||||
log.debug("Simulation: calling simulator");
|
||||
t1 = System.currentTimeMillis();
|
||||
simulatedData = simulator.simulate(simulationConditions);
|
||||
t2 = System.currentTimeMillis();
|
||||
log.debug("Simulation: returning from simulator, simulation took " + (t2 - t1) + "ms");
|
||||
|
||||
// Set simulated info after simulation, will not be set in case of exception
|
||||
simulatedConditions = conditions.clone();
|
||||
simulatedMotors = getConfiguration().getMotorConfigurationDescription();
|
||||
simulatedRocketID = rocket.getFunctionalModID();
|
||||
|
||||
status = Status.UPTODATE;
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
|
||||
@ -294,6 +320,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the conditions used in the previous simulation, or <code>null</code>.
|
||||
*/
|
||||
public GUISimulationConditions getSimulatedConditions() {
|
||||
mutex.verify();
|
||||
return simulatedConditions;
|
||||
}
|
||||
|
||||
@ -306,6 +333,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @see FlightData#getWarningSet()
|
||||
*/
|
||||
public WarningSet getSimulatedWarnings() {
|
||||
mutex.verify();
|
||||
if (simulatedData == null)
|
||||
return null;
|
||||
return simulatedData.getWarningSet();
|
||||
@ -321,6 +349,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @see Rocket#getMotorConfigurationNameOrDescription(String)
|
||||
*/
|
||||
public String getSimulatedMotorDescription() {
|
||||
mutex.verify();
|
||||
return simulatedMotors;
|
||||
}
|
||||
|
||||
@ -331,6 +360,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
* @return the flight data of the previous simulation, or <code>null</code>.
|
||||
*/
|
||||
public FlightData getSimulatedData() {
|
||||
mutex.verify();
|
||||
return simulatedData;
|
||||
}
|
||||
|
||||
@ -344,6 +374,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Simulation copy() {
|
||||
mutex.lock("copy");
|
||||
try {
|
||||
|
||||
Simulation copy = (Simulation) super.clone();
|
||||
@ -359,9 +390,10 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
|
||||
return copy;
|
||||
|
||||
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new BugException("Clone not supported, BUG", e);
|
||||
} finally {
|
||||
mutex.unlock("copy");
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,26 +407,33 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Simulation duplicateSimulation(Rocket newRocket) {
|
||||
Simulation copy = new Simulation(newRocket);
|
||||
|
||||
copy.name = this.name;
|
||||
copy.conditions.copyFrom(this.conditions);
|
||||
copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
|
||||
copy.simulationStepperClass = this.simulationStepperClass;
|
||||
copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
|
||||
|
||||
return copy;
|
||||
mutex.lock("duplicateSimulation");
|
||||
try {
|
||||
Simulation copy = new Simulation(newRocket);
|
||||
|
||||
copy.name = this.name;
|
||||
copy.conditions.copyFrom(this.conditions);
|
||||
copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
|
||||
copy.simulationStepperClass = this.simulationStepperClass;
|
||||
copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
|
||||
|
||||
return copy;
|
||||
} finally {
|
||||
mutex.unlock("duplicateSimulation");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void addChangeListener(ChangeListener listener) {
|
||||
mutex.verify();
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChangeListener(ChangeListener listener) {
|
||||
mutex.verify();
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,8 @@ public class DebugLogDialog extends JDialog {
|
||||
panel.add(new JLabel("Display log lines:"), "gapright para, split");
|
||||
for (LogLevel l : LogLevel.values()) {
|
||||
JCheckBox box = new JCheckBox(l.toString());
|
||||
box.setSelected(true);
|
||||
// By default display DEBUG and above
|
||||
box.setSelected(l.atLeast(LogLevel.DEBUG));
|
||||
box.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@ -279,6 +280,7 @@ public class DebugLogDialog extends JDialog {
|
||||
sorter.setComparator(1, NumericComparator.INSTANCE);
|
||||
sorter.setComparator(4, new LocationComparator());
|
||||
table.setRowSorter(sorter);
|
||||
sorter.setRowFilter(new LogFilter());
|
||||
|
||||
|
||||
panel.add(new JScrollPane(table), "span, grow, width " +
|
||||
|
@ -3,16 +3,31 @@ package net.sf.openrocket.gui.dialogs;
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
|
||||
public class DetailDialog {
|
||||
|
||||
public static void showDetailedMessageDialog(Component parentComponent, Object message,
|
||||
String details, String title, int messageType) {
|
||||
|
||||
public static void showDetailedMessageDialog(Component parentComponent, Object message,
|
||||
String details, String title, int messageType) {
|
||||
|
||||
// TODO: HIGH: Detailed dialog
|
||||
JOptionPane.showMessageDialog(parentComponent, message, title, messageType, null);
|
||||
if (details != null) {
|
||||
JTextArea textArea = null;
|
||||
textArea = new JTextArea(5, 40);
|
||||
textArea.setText(details);
|
||||
textArea.setCaretPosition(0);
|
||||
textArea.setEditable(false);
|
||||
GUIUtil.changeFontSize(textArea, -2);
|
||||
JOptionPane.showMessageDialog(parentComponent,
|
||||
new Object[] { message, new JScrollPane(textArea) },
|
||||
title, messageType, null);
|
||||
} else {
|
||||
JOptionPane.showMessageDialog(parentComponent, message, title, messageType, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -12,15 +12,15 @@ import java.util.ServiceLoader;
|
||||
import javax.swing.JDialog;
|
||||
|
||||
import net.sf.openrocket.document.OpenRocketDocument;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationParameter;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationParameterService;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameterService;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifierService;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
||||
public class GeneralOptimizationDialog extends JDialog {
|
||||
|
||||
private final List<RocketOptimizationParameter> optimizationParameters = new ArrayList<RocketOptimizationParameter>();
|
||||
private final List<OptimizableParameter> optimizationParameters = new ArrayList<OptimizableParameter>();
|
||||
private final Map<Object, List<SimulationModifier>> simulationModifiers =
|
||||
new HashMap<Object, List<SimulationModifier>>();
|
||||
|
||||
@ -36,10 +36,10 @@ public class GeneralOptimizationDialog extends JDialog {
|
||||
|
||||
|
||||
private void loadOptimizationParameters() {
|
||||
ServiceLoader<RocketOptimizationParameterService> loader =
|
||||
ServiceLoader.load(RocketOptimizationParameterService.class);
|
||||
ServiceLoader<OptimizableParameterService> loader =
|
||||
ServiceLoader.load(OptimizableParameterService.class);
|
||||
|
||||
for (RocketOptimizationParameterService g : loader) {
|
||||
for (OptimizableParameterService g : loader) {
|
||||
optimizationParameters.addAll(g.getParameters(document));
|
||||
}
|
||||
|
||||
@ -47,9 +47,9 @@ public class GeneralOptimizationDialog extends JDialog {
|
||||
throw new BugException("No rocket optimization parameters found, distribution built wrong.");
|
||||
}
|
||||
|
||||
Collections.sort(optimizationParameters, new Comparator<RocketOptimizationParameter>() {
|
||||
Collections.sort(optimizationParameters, new Comparator<OptimizableParameter>() {
|
||||
@Override
|
||||
public int compare(RocketOptimizationParameter o1, RocketOptimizationParameter o2) {
|
||||
public int compare(OptimizableParameter o1, OptimizableParameter o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
|
@ -25,6 +25,8 @@ import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.swing.Action;
|
||||
@ -69,6 +71,7 @@ import net.sf.openrocket.gui.dialogs.AboutDialog;
|
||||
import net.sf.openrocket.gui.dialogs.BugReportDialog;
|
||||
import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
|
||||
import net.sf.openrocket.gui.dialogs.DebugLogDialog;
|
||||
import net.sf.openrocket.gui.dialogs.DetailDialog;
|
||||
import net.sf.openrocket.gui.dialogs.ExampleDesignDialog;
|
||||
import net.sf.openrocket.gui.dialogs.LicenseDialog;
|
||||
import net.sf.openrocket.gui.dialogs.MotorDatabaseLoadingDialog;
|
||||
@ -87,6 +90,8 @@ import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.GUIUtil;
|
||||
import net.sf.openrocket.util.Icons;
|
||||
import net.sf.openrocket.util.MemoryManagement;
|
||||
import net.sf.openrocket.util.MemoryManagement.MemoryData;
|
||||
import net.sf.openrocket.util.OpenFileWorker;
|
||||
import net.sf.openrocket.util.Prefs;
|
||||
import net.sf.openrocket.util.Reflection;
|
||||
@ -728,10 +733,86 @@ public class BasicFrame extends JFrame {
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
|
||||
item = new JMenuItem("Memory statistics");
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.user("Memory statistics selected");
|
||||
|
||||
// Get discarded but remaining objects (this also runs System.gc multiple times)
|
||||
List<MemoryData> objects = MemoryManagement.getRemainingObjects();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Objects that should have been garbage-collected but have not been:\n");
|
||||
int count = 0;
|
||||
for (MemoryData data : objects) {
|
||||
Object o = data.getReference().get();
|
||||
if (o == null)
|
||||
continue;
|
||||
sb.append("Age ").append(System.currentTimeMillis() - data.getRegistrationTime())
|
||||
.append(" ms: ").append(o).append('\n');
|
||||
count++;
|
||||
// Explicitly null the strong reference to avoid possibility of invisible references
|
||||
o = null;
|
||||
}
|
||||
sb.append("Total: " + count);
|
||||
|
||||
// Get basic memory stats
|
||||
System.gc();
|
||||
long max = Runtime.getRuntime().maxMemory();
|
||||
long free = Runtime.getRuntime().freeMemory();
|
||||
long used = max - free;
|
||||
String[] stats = new String[4];
|
||||
stats[0] = "Memory usage:";
|
||||
stats[1] = String.format(" Max memory: %.1f MB", max / 1024.0 / 1024.0);
|
||||
stats[2] = String.format(" Used memory: %.1f MB (%.0f%%)", used / 1024.0 / 1024.0, 100.0 * used / max);
|
||||
stats[3] = String.format(" Free memory: %.1f MB (%.0f%%)", free / 1024.0 / 1024.0, 100.0 * free / max);
|
||||
|
||||
|
||||
DetailDialog.showDetailedMessageDialog(BasicFrame.this, stats, sb.toString(),
|
||||
"Memory statistics", JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
|
||||
item = new JMenuItem("Exhaust memory");
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.user("Exhaust memory selected");
|
||||
LinkedList<byte[]> data = new LinkedList<byte[]>();
|
||||
int count = 0;
|
||||
final int bytesPerArray = 10240;
|
||||
try {
|
||||
while (true) {
|
||||
byte[] array = new byte[bytesPerArray];
|
||||
for (int i = 0; i < bytesPerArray; i++) {
|
||||
array[i] = (byte) i;
|
||||
}
|
||||
data.add(array);
|
||||
count++;
|
||||
}
|
||||
} catch (OutOfMemoryError error) {
|
||||
data = null;
|
||||
long size = bytesPerArray * (long) count;
|
||||
String s = String.format("OutOfMemory occurred after %d iterations (approx. %.1f MB consumed)",
|
||||
count, size / 1024.0 / 1024.0);
|
||||
log.debug(s, error);
|
||||
JOptionPane.showMessageDialog(BasicFrame.this, s);
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem("Exception here");
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.user("Exception here selected");
|
||||
throw new RuntimeException("Testing exception from menu action listener");
|
||||
@ -769,13 +850,22 @@ public class BasicFrame extends JFrame {
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem("OutOfMemoryError here");
|
||||
item.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.user("OutOfMemoryError here selected");
|
||||
throw new OutOfMemoryError("Testing OutOfMemoryError from menu action listener");
|
||||
}
|
||||
});
|
||||
menu.add(item);
|
||||
|
||||
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Select the tab on the main pane.
|
||||
*
|
||||
|
@ -5,8 +5,8 @@ import javax.swing.SwingUtilities;
|
||||
|
||||
import net.sf.openrocket.gui.dialogs.BugReportDialog;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.logging.TraceException;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
||||
|
||||
public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
@ -63,6 +63,7 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
} else {
|
||||
log.info("Exception handler not on EDT, invoking dialog on EDT");
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showDialog(thread, throwable);
|
||||
}
|
||||
@ -76,7 +77,7 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
log.error("Caught exception while handling exception", ex);
|
||||
System.err.println("Exception in exception handler, dumping exception:");
|
||||
ex.printStackTrace();
|
||||
} catch (Throwable ignore) {
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
} finally {
|
||||
@ -90,11 +91,14 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
/**
|
||||
* Handle an error condition programmatically without throwing an exception.
|
||||
* This can be used in cases where recovery of the error is desirable.
|
||||
* <p>
|
||||
* This method is guaranteed never to throw an exception, and can thus be safely
|
||||
* used in finally blocks.
|
||||
*
|
||||
* @param message the error message.
|
||||
*/
|
||||
public static void handleErrorCondition(String message) {
|
||||
log.error(1, message);
|
||||
log.error(1, message, new TraceException());
|
||||
handleErrorCondition(new InternalException(message));
|
||||
}
|
||||
|
||||
@ -102,6 +106,9 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
/**
|
||||
* Handle an error condition programmatically without throwing an exception.
|
||||
* This can be used in cases where recovery of the error is desirable.
|
||||
* <p>
|
||||
* This method is guaranteed never to throw an exception, and can thus be safely
|
||||
* used in finally blocks.
|
||||
*
|
||||
* @param message the error message.
|
||||
* @param exception the exception that occurred.
|
||||
@ -115,36 +122,39 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
/**
|
||||
* Handle an error condition programmatically without throwing an exception.
|
||||
* This can be used in cases where recovery of the error is desirable.
|
||||
* <p>
|
||||
* This method is guaranteed never to throw an exception, and can thus be safely
|
||||
* used in finally blocks.
|
||||
*
|
||||
* @param exception the exception that occurred.
|
||||
*/
|
||||
public static void handleErrorCondition(final Exception exception) {
|
||||
if (!(exception instanceof InternalException)) {
|
||||
log.error(1, "Error occurred", exception);
|
||||
}
|
||||
final Thread thread = Thread.currentThread();
|
||||
final ExceptionHandler handler = instance;
|
||||
|
||||
if (handler == null) {
|
||||
// Not initialized, throw the exception
|
||||
throw new BugException("Error condition before exception handling has been initialized", exception);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!(exception instanceof InternalException)) {
|
||||
log.error(1, "Error occurred", exception);
|
||||
}
|
||||
final Thread thread = Thread.currentThread();
|
||||
final ExceptionHandler handler = instance;
|
||||
|
||||
if (handler == null) {
|
||||
log.error("Error condition occurred before exception handling has been initialized", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
log.info("Running in EDT, showing dialog");
|
||||
handler.showDialog(thread, exception);
|
||||
} else {
|
||||
log.info("Not in EDT, invoking and waiting for dialog");
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handler.showDialog(thread, exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while showing error dialog", e);
|
||||
e.printStackTrace();
|
||||
log.error("Exception occurred in error handler", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,6 +299,8 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
*/
|
||||
private static boolean isNonFatalJREBug(Throwable t) {
|
||||
|
||||
// NOTE: Calling method logs the entire throwable, so log only message here
|
||||
|
||||
/*
|
||||
* Detect and ignore bug 6828938 in Sun JRE 1.6.0_14 - 1.6.0_16.
|
||||
*/
|
||||
@ -346,6 +358,18 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect Sun JRE bug in D3D
|
||||
*/
|
||||
if (t instanceof ClassCastException) {
|
||||
if (t.getMessage().equals("sun.awt.Win32GraphicsConfig cannot be cast to sun.java2d.d3d.D3DGraphicsConfig")) {
|
||||
log.warn("Ignoring Sun JRE bug " +
|
||||
"(see http://forums.sun.com/thread.jspa?threadID=5440525): " + t);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,6 @@ public class SimulationPanel extends JPanel {
|
||||
|
||||
// Set simulation status icon
|
||||
Simulation.Status status = document.getSimulation(row).getStatus();
|
||||
System.out.println("status=" + status);
|
||||
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
|
||||
|
||||
|
||||
|
@ -69,7 +69,13 @@ public class SimulationRunDialog extends JDialog {
|
||||
private final JProgressBar progressBar;
|
||||
|
||||
|
||||
/*
|
||||
* NOTE: Care must be used when accessing the simulation parameters, since they
|
||||
* are being run in another thread. Mutexes are used to avoid concurrent usage, which
|
||||
* will result in an exception being thrown!
|
||||
*/
|
||||
private final Simulation[] simulations;
|
||||
private final String[] simulationNames;
|
||||
private final SimulationWorker[] simulationWorkers;
|
||||
private final SimulationStatus[] simulationStatuses;
|
||||
private final double[] simulationMaxAltitude;
|
||||
@ -87,6 +93,7 @@ public class SimulationRunDialog extends JDialog {
|
||||
|
||||
// Initialize the simulations
|
||||
int n = simulations.length;
|
||||
simulationNames = new String[n];
|
||||
simulationWorkers = new SimulationWorker[n];
|
||||
simulationStatuses = new SimulationStatus[n];
|
||||
simulationMaxAltitude = new double[n];
|
||||
@ -94,6 +101,7 @@ public class SimulationRunDialog extends JDialog {
|
||||
simulationDone = new boolean[n];
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
simulationNames[i] = simulations[i].getName();
|
||||
simulationWorkers[i] = new InteractiveSimulationWorker(simulations[i], i);
|
||||
executor.execute(simulationWorkers[i]);
|
||||
}
|
||||
@ -201,7 +209,7 @@ public class SimulationRunDialog extends JDialog {
|
||||
log.debug("Progressbar value " + progress);
|
||||
|
||||
// Update the simulation fields
|
||||
simLabel.setText("Running " + simulations[index].getName());
|
||||
simLabel.setText("Running " + simulationNames[index]);
|
||||
if (simulationStatuses[index] == null) {
|
||||
log.debug("No simulation status data available, setting empty labels");
|
||||
timeLabel.setText("");
|
||||
|
@ -54,8 +54,6 @@ public abstract class SimulationWorker extends SwingWorker<FlightData, Simulatio
|
||||
try {
|
||||
simulation.simulate(listeners);
|
||||
} catch (Throwable e) {
|
||||
// System.out.println("Simulation interrupted:");
|
||||
// e.printStackTrace();
|
||||
throwable = e;
|
||||
return null;
|
||||
}
|
||||
@ -77,8 +75,6 @@ public abstract class SimulationWorker extends SwingWorker<FlightData, Simulatio
|
||||
/**
|
||||
* Called after a simulation is successfully simulated. This method is not
|
||||
* called if the simulation ends in an exception.
|
||||
*
|
||||
* @param sim the simulation including the flight data
|
||||
*/
|
||||
protected abstract void simulationDone();
|
||||
|
||||
|
@ -608,7 +608,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
@Override
|
||||
protected void simulationDone() {
|
||||
// Do nothing if cancelled
|
||||
if (isCancelled() || backgroundSimulationWorker != this) // Double-check
|
||||
if (isCancelled() || backgroundSimulationWorker != this)
|
||||
return;
|
||||
|
||||
backgroundSimulationWorker = null;
|
||||
@ -658,6 +658,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
* Updates the selection in the FigureParameters and repaints the figure.
|
||||
* Ignores the event itself.
|
||||
*/
|
||||
@Override
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
TreePath[] paths = selectionModel.getSelectionPaths();
|
||||
if (paths == null) {
|
||||
@ -688,6 +689,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
figure.addChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
boolean state = (Boolean) getValue(Action.SELECTED_KEY);
|
||||
if (state == true) {
|
||||
@ -698,6 +700,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
stateChanged(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
putValue(Action.SELECTED_KEY, figure.getType() == type);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ import net.sf.openrocket.util.BugException;
|
||||
* <li><code>message</code> the String message (may be null).
|
||||
* <li><code>cause</code> the exception that caused this log (may be null).
|
||||
* </ul>
|
||||
* <p>
|
||||
* The logging methods are guaranteed never to throw an exception, and can thus be safely
|
||||
* used in finally blocks.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
@ -56,7 +59,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void verbose(String message) {
|
||||
log(createLogLine(0, LogLevel.VBOSE, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.VBOSE, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +73,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void verbose(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.VBOSE, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.VBOSE, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +87,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void verbose(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.VBOSE, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.VBOSE, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +102,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void verbose(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.VBOSE, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.VBOSE, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -97,7 +116,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void debug(String message) {
|
||||
log(createLogLine(0, LogLevel.DEBUG, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.DEBUG, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,7 +130,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void debug(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.DEBUG, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.DEBUG, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +144,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void debug(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.DEBUG, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.DEBUG, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,7 +159,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void debug(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.DEBUG, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.DEBUG, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -138,7 +173,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void info(String message) {
|
||||
log(createLogLine(0, LogLevel.INFO, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.INFO, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,7 +187,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void info(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.INFO, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.INFO, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,7 +201,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void info(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.INFO, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.INFO, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,7 +216,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void info(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.INFO, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.INFO, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -179,7 +230,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void user(String message) {
|
||||
log(createLogLine(0, LogLevel.USER, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.USER, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +244,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void user(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.USER, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.USER, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,7 +258,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void user(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.USER, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.USER, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +273,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void user(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.USER, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.USER, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -220,7 +287,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void warn(String message) {
|
||||
log(createLogLine(0, LogLevel.WARN, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.WARN, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -230,7 +301,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void warn(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.WARN, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.WARN, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,7 +315,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void warn(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.WARN, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.WARN, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -251,7 +330,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void warn(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.WARN, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.WARN, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -261,7 +344,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void error(String message) {
|
||||
log(createLogLine(0, LogLevel.ERROR, message, null));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.ERROR, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -271,7 +358,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void error(String message, Throwable cause) {
|
||||
log(createLogLine(0, LogLevel.ERROR, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, LogLevel.ERROR, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -281,7 +372,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void error(int levels, String message) {
|
||||
log(createLogLine(levels, LogLevel.ERROR, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.ERROR, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -292,7 +387,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void error(int levels, String message, Throwable cause) {
|
||||
log(createLogLine(levels, LogLevel.ERROR, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, LogLevel.ERROR, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -304,7 +403,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void log(LogLevel level, String message) {
|
||||
log(createLogLine(0, level, message, null));
|
||||
try {
|
||||
log(createLogLine(0, level, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +418,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void log(LogLevel level, String message, Throwable cause) {
|
||||
log(createLogLine(0, level, message, cause));
|
||||
try {
|
||||
log(createLogLine(0, level, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -326,7 +433,11 @@ public abstract class LogHelper {
|
||||
* @param message the logged message (may be null).
|
||||
*/
|
||||
public void log(int levels, LogLevel level, String message) {
|
||||
log(createLogLine(levels, level, message, null));
|
||||
try {
|
||||
log(createLogLine(levels, level, message, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -338,7 +449,11 @@ public abstract class LogHelper {
|
||||
* @param cause the causing exception (may be null).
|
||||
*/
|
||||
public void log(int levels, LogLevel level, String message, Throwable cause) {
|
||||
log(createLogLine(levels, level, message, cause));
|
||||
try {
|
||||
log(createLogLine(levels, level, message, cause));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,15 +53,23 @@ public enum LogLevel {
|
||||
*/
|
||||
VBOSE;
|
||||
|
||||
|
||||
/** The log level with highest priority */
|
||||
public static final LogLevel HIGHEST;
|
||||
/** The log level with lowest priority */
|
||||
public static final LogLevel LOWEST;
|
||||
/** The maximum length of a level textual description */
|
||||
public static final int LENGTH;
|
||||
|
||||
static {
|
||||
int length = 0;
|
||||
for (LogLevel l : LogLevel.values()) {
|
||||
length = Math.max(length, l.toString().length());
|
||||
}
|
||||
LENGTH = length;
|
||||
|
||||
LogLevel[] values = LogLevel.values();
|
||||
HIGHEST = values[0];
|
||||
LOWEST = values[values.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,12 +54,12 @@ public class LogLevelBufferLogger extends LogHelper {
|
||||
|
||||
if (misses > 0) {
|
||||
if (logs.isEmpty()) {
|
||||
result.add(new LogLine(level, 0, 0, new TraceException(),
|
||||
"--- " + misses + " " + level + " lines removed but log is empty! ---",
|
||||
result.add(new LogLine(level, 0, 0, null,
|
||||
"===== " + misses + " " + level + " lines removed but log is empty! =====",
|
||||
null));
|
||||
} else {
|
||||
result.add(new LogLine(level, logs.get(0).getLogCount(), 0, new TraceException(),
|
||||
"--- " + misses + " " + level + " lines removed ---", null));
|
||||
result.add(new LogLine(level, logs.get(0).getLogCount(), 0, null,
|
||||
"===== " + misses + " " + level + " lines removed =====", null));
|
||||
}
|
||||
}
|
||||
result.addAll(logs);
|
||||
|
@ -31,13 +31,32 @@ public class LogLine implements Comparable<LogLine> {
|
||||
|
||||
private volatile String formattedMessage = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a LogLine at the current moment. The next log line count number is selected
|
||||
* and the current run time set to the timestamp.
|
||||
*
|
||||
* @param level the logging level
|
||||
* @param trace the trace exception for the log line, <code>null</code> permitted
|
||||
* @param message the log message
|
||||
* @param cause the causing throwable, <code>null</code> permitted
|
||||
*/
|
||||
public LogLine(LogLevel level, TraceException trace, String message, Throwable cause) {
|
||||
this(level, logCount.getAndIncrement(), System.currentTimeMillis() - startTime, trace, message, cause);
|
||||
}
|
||||
|
||||
|
||||
public LogLine(LogLevel level, int count, long timestamp,
|
||||
/**
|
||||
* Construct a LogLine with all parameters. This should only be used in special conditions,
|
||||
* for example to insert a log line at a specific point within normal logs.
|
||||
*
|
||||
* @param level the logging level
|
||||
* @param count the log line count number
|
||||
* @param timestamp the log line timestamp
|
||||
* @param trace the trace exception for the log line, <code>null</code> permitted
|
||||
* @param message the log message
|
||||
* @param cause the causing throwable, <code>null</code> permitted
|
||||
*/
|
||||
public LogLine(LogLevel level, int count, long timestamp,
|
||||
TraceException trace, String message, Throwable cause) {
|
||||
this.level = level;
|
||||
this.count = count;
|
||||
@ -46,57 +65,57 @@ public class LogLine implements Comparable<LogLine> {
|
||||
this.message = message;
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the level
|
||||
*/
|
||||
public LogLevel getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the count
|
||||
*/
|
||||
public int getLogCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the timestamp
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the trace
|
||||
*/
|
||||
public TraceException getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return the error
|
||||
*/
|
||||
public Throwable getCause() {
|
||||
return cause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@ -109,7 +128,7 @@ public class LogLine implements Comparable<LogLine> {
|
||||
if (formattedMessage == null) {
|
||||
String str;
|
||||
str = String.format("%4d %10.3f %-" + LogLevel.LENGTH + "s %s %s",
|
||||
count, timestamp/1000.0, (level != null) ? level.toString() : "NULL",
|
||||
count, timestamp / 1000.0, (level != null) ? level.toString() : "NULL",
|
||||
(trace != null) ? trace.getMessage() : "(-)",
|
||||
message);
|
||||
if (cause != null) {
|
||||
@ -123,8 +142,8 @@ public class LogLine implements Comparable<LogLine> {
|
||||
}
|
||||
return formattedMessage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compare against another log line based on the log line count number.
|
||||
*/
|
||||
|
@ -62,6 +62,17 @@ public class TraceException extends Exception {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct an exception with the specified message.
|
||||
*
|
||||
* @param message the message for the exception.
|
||||
*/
|
||||
public TraceException(String message) {
|
||||
this(0, 0);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the description of the code position as provided in the constructor.
|
||||
*/
|
||||
|
@ -18,23 +18,8 @@ public interface Function {
|
||||
* @param point the point at which to evaluate the function.
|
||||
* @return the function value.
|
||||
* @throws InterruptedException if the thread was interrupted before function evaluation was completed.
|
||||
* @throws OptimizationException if an error occurs that prevents the optimization
|
||||
*/
|
||||
public double evaluate(Point point) throws InterruptedException;
|
||||
|
||||
|
||||
/**
|
||||
* Return a cached value of the function at the specified point. This allows efficient
|
||||
* caching of old values even between calls to optimization methods. This method should
|
||||
* NOT evaluate the function except in special cases (e.g. the point is outside of the
|
||||
* function domain).
|
||||
* <p>
|
||||
* Note that it is allowed to always allowed to return <code>Double.NaN</code>, especially
|
||||
* for functions that are fast to evaluate.
|
||||
*
|
||||
* @param point the point of function evaluation.
|
||||
* @return the function value, or <code>Double.NaN</code> if the function value has not been
|
||||
* evaluated at this point.
|
||||
*/
|
||||
public double preComputed(Point point);
|
||||
public double evaluate(Point point) throws InterruptedException, OptimizationException;
|
||||
|
||||
}
|
||||
|
@ -2,18 +2,38 @@ package net.sf.openrocket.optimization.general;
|
||||
|
||||
/**
|
||||
* A storage of cached values of a function. The purpose of this class is to
|
||||
* cache function values
|
||||
* cache function values between optimization runs. Subinterfaces may provide
|
||||
* additional functionality.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public interface FunctionCache {
|
||||
|
||||
/**
|
||||
* Compute and return the value of the function at the specified point.
|
||||
*
|
||||
* @param point the point at which to evaluate.
|
||||
* @return the value of the function at that point.
|
||||
*/
|
||||
public double getValue(Point point);
|
||||
|
||||
/**
|
||||
* Clear the cache.
|
||||
*/
|
||||
public void clearCache();
|
||||
|
||||
/**
|
||||
* Return the function that is evaluated by this cache implementation.
|
||||
*
|
||||
* @return the function that is being evaluated.
|
||||
*/
|
||||
public Function getFunction();
|
||||
|
||||
/**
|
||||
* Set the function that is evaluated by this cache implementation.
|
||||
*
|
||||
* @param function the function that is being evaluated.
|
||||
*/
|
||||
public void setFunction(Function function);
|
||||
|
||||
}
|
||||
|
@ -14,8 +14,9 @@ public interface FunctionOptimizer {
|
||||
*
|
||||
* @param initial the initial start point of the optimization.
|
||||
* @param control the optimization control.
|
||||
* @throws OptimizationException if an error occurs that prevents optimization
|
||||
*/
|
||||
public void optimize(Point initial, OptimizationController control);
|
||||
public void optimize(Point initial, OptimizationController control) throws OptimizationException;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,22 @@
|
||||
package net.sf.openrocket.optimization.general;
|
||||
|
||||
/**
|
||||
* An exception that prevents optimization from continuing.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class OptimizationException extends Exception {
|
||||
|
||||
public OptimizationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public OptimizationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public OptimizationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
@ -14,11 +14,17 @@ import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.sf.openrocket.util.BugException;
|
||||
|
||||
/**
|
||||
* An implementation of a ParallelFunctionCache that evaluates function values
|
||||
* in parallel and caches them. This allows pre-calculating possibly required
|
||||
* function values beforehand. If values are not required after all, the
|
||||
* computation can be aborted assuming the function evaluation supports it.
|
||||
* <p>
|
||||
* Note that while this class handles threads and abstracts background execution,
|
||||
* the public methods themselves are NOT thread-safe and should be called from
|
||||
* only one thread at a time.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
@ -32,13 +38,22 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
private Function function;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a cache that uses the same number of computational threads as there are
|
||||
* processors available.
|
||||
*/
|
||||
public ParallelExecutorCache() {
|
||||
this(Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cache that uses the specified number of computational threads for background
|
||||
* computation. The threads that are created are marked as daemon threads.
|
||||
*
|
||||
* @param threadCount the number of threads to use in the executor.
|
||||
*/
|
||||
public ParallelExecutorCache(int threadCount) {
|
||||
executor = new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
|
||||
this(new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(),
|
||||
new ThreadFactory() {
|
||||
@Override
|
||||
@ -47,20 +62,22 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a cache that uses the specified ExecutorService for managing
|
||||
* computational threads.
|
||||
*
|
||||
* @param executor the executor to use for function evaluations.
|
||||
*/
|
||||
public ParallelExecutorCache(ExecutorService executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Queue a list of function evaluations at the specified points.
|
||||
*
|
||||
* @param points the points at which to evaluate the function.
|
||||
*/
|
||||
@Override
|
||||
public void compute(Collection<Point> points) {
|
||||
for (Point p : points) {
|
||||
compute(p);
|
||||
@ -68,11 +85,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queue function evaluation for the specified point.
|
||||
*
|
||||
* @param point the point at which to evaluate the function.
|
||||
*/
|
||||
@Override
|
||||
public void compute(Point point) {
|
||||
if (functionCache.containsKey(point)) {
|
||||
// Function has already been evaluated at the point
|
||||
@ -84,13 +97,6 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
return;
|
||||
}
|
||||
|
||||
double value = function.preComputed(point);
|
||||
if (!Double.isNaN(value)) {
|
||||
// Function value was in function cache
|
||||
functionCache.put(point, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Submit point for evaluation
|
||||
FunctionCallable callable = new FunctionCallable(function, point);
|
||||
Future<Double> future = executor.submit(callable);
|
||||
@ -98,27 +104,16 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wait for a collection of points to be computed. After calling this method
|
||||
* the function values are available by calling XXX
|
||||
*
|
||||
* @param points the points to wait for.
|
||||
* @throws InterruptedException if this thread was interrupted while waiting.
|
||||
*/
|
||||
public void waitFor(Collection<Point> points) throws InterruptedException {
|
||||
@Override
|
||||
public void waitFor(Collection<Point> points) throws InterruptedException, OptimizationException {
|
||||
for (Point p : points) {
|
||||
waitFor(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a point to be computed. After calling this method
|
||||
* the function values are available by calling XXX
|
||||
*
|
||||
* @param point the point to wait for.
|
||||
* @throws InterruptedException if this thread was interrupted while waiting.
|
||||
*/
|
||||
public void waitFor(Point point) throws InterruptedException {
|
||||
|
||||
@Override
|
||||
public void waitFor(Point point) throws InterruptedException, OptimizationException {
|
||||
if (functionCache.containsKey(point)) {
|
||||
return;
|
||||
}
|
||||
@ -132,18 +127,24 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
double value = future.get();
|
||||
functionCache.put(point, value);
|
||||
} catch (ExecutionException e) {
|
||||
throw new IllegalStateException("Function threw exception while processing", e.getCause());
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof InterruptedException) {
|
||||
throw (InterruptedException) cause;
|
||||
}
|
||||
if (cause instanceof OptimizationException) {
|
||||
throw (OptimizationException) cause;
|
||||
}
|
||||
if (cause instanceof RuntimeException) {
|
||||
throw (RuntimeException) cause;
|
||||
}
|
||||
|
||||
throw new BugException("Function threw unknown exception while processing", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abort the computation of the specified point. If computation has ended,
|
||||
* the result is stored in the function cache anyway.
|
||||
*
|
||||
* @param points the points to abort.
|
||||
* @return a list of the points that have been computed anyway
|
||||
*/
|
||||
|
||||
@Override
|
||||
public List<Point> abort(Collection<Point> points) {
|
||||
List<Point> computed = new ArrayList<Point>(Math.min(points.size(), 10));
|
||||
|
||||
@ -157,13 +158,8 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abort the computation of the specified point. If computation has ended,
|
||||
* the result is stored in the function cache anyway.
|
||||
*
|
||||
* @param point the point to abort.
|
||||
* @return <code>true</code> if the point has been computed anyway, <code>false</code> if not.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean abort(Point point) {
|
||||
if (functionCache.containsKey(point)) {
|
||||
return true;
|
||||
@ -191,17 +187,17 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getValue(Point point) {
|
||||
Double d = functionCache.get(point);
|
||||
if (d == null) {
|
||||
throw new IllegalStateException(point.toString() + " is not in function cache. " +
|
||||
throw new IllegalStateException(point + " is not in function cache. " +
|
||||
"functionCache=" + functionCache + " futureMap=" + futureMap);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Function getFunction() {
|
||||
return function;
|
||||
@ -220,6 +216,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
functionCache.clear();
|
||||
}
|
||||
|
||||
|
||||
public ExecutorService getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
@ -239,7 +236,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double call() throws InterruptedException {
|
||||
public Double call() throws InterruptedException, OptimizationException {
|
||||
return calledFunction.evaluate(point);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A FunctionCache that allows queuing points to be computed in the background,
|
||||
* A FunctionCache that allows scheduling points to be computed in the background,
|
||||
* waiting for specific points to become computed or aborting the computation of
|
||||
* points.
|
||||
*
|
||||
@ -13,14 +13,17 @@ import java.util.List;
|
||||
public interface ParallelFunctionCache extends FunctionCache {
|
||||
|
||||
/**
|
||||
* Queue a list of function evaluations at the specified points.
|
||||
* Schedule a list of function evaluations at the specified points.
|
||||
* The points are added to the end of the computation queue in the order
|
||||
* they are returned by the iterator.
|
||||
*
|
||||
* @param points the points at which to evaluate the function.
|
||||
*/
|
||||
public void compute(Collection<Point> points);
|
||||
|
||||
/**
|
||||
* Queue function evaluation for the specified point.
|
||||
* Schedule function evaluation for the specified point. The point is
|
||||
* added to the end of the computation queue.
|
||||
*
|
||||
* @param point the point at which to evaluate the function.
|
||||
*/
|
||||
@ -28,25 +31,26 @@ public interface ParallelFunctionCache extends FunctionCache {
|
||||
|
||||
/**
|
||||
* Wait for a collection of points to be computed. After calling this method
|
||||
* the function values are available by calling XXX
|
||||
* the function values are available by calling {@link #getValue(Point)}.
|
||||
*
|
||||
* @param points the points to wait for.
|
||||
* @throws InterruptedException if this thread was interrupted while waiting.
|
||||
* @throws InterruptedException if this thread or the computing thread was interrupted while waiting.
|
||||
*/
|
||||
public void waitFor(Collection<Point> points) throws InterruptedException;
|
||||
public void waitFor(Collection<Point> points) throws InterruptedException, OptimizationException;
|
||||
|
||||
/**
|
||||
* Wait for a point to be computed. After calling this method
|
||||
* the function values are available by calling XXX
|
||||
* the function value is available by calling {@link #getValue(Point)}.
|
||||
*
|
||||
* @param point the point to wait for.
|
||||
* @throws InterruptedException if this thread was interrupted while waiting.
|
||||
* @throws InterruptedException if this thread or the computing thread was interrupted while waiting.
|
||||
* @throws OptimizationException
|
||||
*/
|
||||
public void waitFor(Point point) throws InterruptedException;
|
||||
public void waitFor(Point point) throws InterruptedException, OptimizationException;
|
||||
|
||||
|
||||
/**
|
||||
* Abort the computation of the specified point. If computation has ended,
|
||||
* Abort the computation of the specified points. If computation has ended,
|
||||
* the result is stored in the function cache anyway.
|
||||
*
|
||||
* @param points the points to abort.
|
||||
|
@ -9,6 +9,7 @@ import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.optimization.general.FunctionCache;
|
||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
@ -48,7 +49,7 @@ public class MultidirectionalSearchOptimizer implements FunctionOptimizer, Stati
|
||||
|
||||
|
||||
@Override
|
||||
public void optimize(Point initial, OptimizationController control) {
|
||||
public void optimize(Point initial, OptimizationController control) throws OptimizationException {
|
||||
FunctionCacheComparator comparator = new FunctionCacheComparator(functionExecutor);
|
||||
|
||||
final List<Point> pattern = SearchPattern.square(initial.dim());
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.sf.openrocket.optimization.rocketoptimization;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
|
||||
/**
|
||||
* A parameter of a rocket or simulation that can be optimized
|
||||
@ -8,7 +9,7 @@ import net.sf.openrocket.document.Simulation;
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public interface RocketOptimizationParameter {
|
||||
public interface OptimizableParameter {
|
||||
|
||||
/**
|
||||
* Return the label name for this optimization parameter.
|
||||
@ -20,10 +21,13 @@ public interface RocketOptimizationParameter {
|
||||
/**
|
||||
* Compute the value for this optimization parameter for the simulation.
|
||||
* The return value can be any double value.
|
||||
* <p>
|
||||
* This method can return NaN in case of a problem computing
|
||||
*
|
||||
* @param simulation the simulation
|
||||
* @return the parameter value (any double value)
|
||||
* @throws OptimizationException if an error occurs preventing the optimization from continuing
|
||||
*/
|
||||
public double computeValue(Simulation simulation);
|
||||
public double computeValue(Simulation simulation) throws OptimizationException;
|
||||
|
||||
}
|
@ -9,7 +9,7 @@ import net.sf.openrocket.document.OpenRocketDocument;
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public interface RocketOptimizationParameterService {
|
||||
public interface OptimizableParameterService {
|
||||
|
||||
/**
|
||||
* Return all available rocket optimization parameters for this document.
|
||||
@ -18,6 +18,6 @@ public interface RocketOptimizationParameterService {
|
||||
* @param document the design document
|
||||
* @return a collection of the rocket optimization parameters.
|
||||
*/
|
||||
public Collection<RocketOptimizationParameter> getParameters(OpenRocketDocument document);
|
||||
public Collection<OptimizableParameter> getParameters(OpenRocketDocument document);
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.optimization.general.Function;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
@ -20,13 +21,16 @@ import net.sf.openrocket.startup.Application;
|
||||
public class RocketOptimizationFunction implements Function {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
|
||||
|
||||
/*
|
||||
* NOTE: This class must be thread-safe!!!
|
||||
*/
|
||||
|
||||
private final Simulation baseSimulation;
|
||||
private final RocketOptimizationParameter parameter;
|
||||
private final OptimizableParameter parameter;
|
||||
private final OptimizationGoal goal;
|
||||
private final SimulationDomain domain;
|
||||
private final SimulationModifier[] modifiers;
|
||||
|
||||
private final Map<Point, Double> parameterValueCache = new ConcurrentHashMap<Point, Double>();
|
||||
@ -44,11 +48,12 @@ public class RocketOptimizationFunction implements Function {
|
||||
* @param goal the goal of the rocket parameter
|
||||
* @param modifiers the modifiers that modify the simulation
|
||||
*/
|
||||
public RocketOptimizationFunction(Simulation baseSimulation, RocketOptimizationParameter parameter,
|
||||
OptimizationGoal goal, SimulationModifier... modifiers) {
|
||||
public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
|
||||
OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
|
||||
this.baseSimulation = baseSimulation;
|
||||
this.parameter = parameter;
|
||||
this.goal = goal;
|
||||
this.domain = domain;
|
||||
this.modifiers = modifiers.clone();
|
||||
if (modifiers.length == 0) {
|
||||
throw new IllegalArgumentException("No SimulationModifiers specified");
|
||||
@ -57,52 +62,71 @@ public class RocketOptimizationFunction implements Function {
|
||||
|
||||
|
||||
@Override
|
||||
public double evaluate(Point point) throws InterruptedException {
|
||||
public double evaluate(Point point) throws InterruptedException, OptimizationException {
|
||||
/*
|
||||
* parameterValue is the computed parameter value (e.g. altitude)
|
||||
* goalValue is the value that needs to be minimized
|
||||
*/
|
||||
double goalValue, parameterValue;
|
||||
|
||||
// Check for precomputed value
|
||||
double value = preComputed(point);
|
||||
if (!Double.isNaN(value)) {
|
||||
return value;
|
||||
Double d = goalValueCache.get(point);
|
||||
if (d != null && !Double.isNaN(d)) {
|
||||
log.verbose("Optimization function value at point " + point + " was found in cache: " + d);
|
||||
return d;
|
||||
}
|
||||
|
||||
log.verbose("Computing optimization function value at point " + point);
|
||||
|
||||
// Create the new simulation based on the point
|
||||
double[] p = point.asArray();
|
||||
if (p.length != modifiers.length) {
|
||||
throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
|
||||
modifiers.length + " simulation modifiers");
|
||||
}
|
||||
Simulation simulation = newSimulationInstance();
|
||||
|
||||
Simulation simulation = newSimulationInstance(baseSimulation);
|
||||
for (int i = 0; i < modifiers.length; i++) {
|
||||
modifiers[i].modify(simulation, p[i]);
|
||||
}
|
||||
|
||||
|
||||
// Check whether the point is within the simulation domain
|
||||
double distance = domain.getDistanceToDomain(simulation);
|
||||
if (distance > 0 || Double.isNaN(distance)) {
|
||||
if (Double.isNaN(distance)) {
|
||||
goalValue = Double.MAX_VALUE;
|
||||
} else {
|
||||
goalValue = (distance + 1) * OUTSIDE_DOMAIN_SCALE;
|
||||
}
|
||||
parameterValueCache.put(point, Double.NaN);
|
||||
goalValueCache.put(point, goalValue);
|
||||
log.verbose("Optimization point is outside of domain, distance=" + distance + " goal function value=" + goalValue);
|
||||
return goalValue;
|
||||
}
|
||||
|
||||
|
||||
// Compute the optimization value
|
||||
value = parameter.computeValue(simulation);
|
||||
parameterValueCache.put(point, value);
|
||||
parameterValue = parameter.computeValue(simulation);
|
||||
parameterValueCache.put(point, parameterValue);
|
||||
|
||||
value = goal.getMinimizationParameter(value);
|
||||
if (Double.isNaN(value)) {
|
||||
log.warn("Computed value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
|
||||
" goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation);
|
||||
value = Double.MAX_VALUE;
|
||||
goalValue = goal.getMinimizationParameter(parameterValue);
|
||||
if (Double.isNaN(goalValue)) {
|
||||
log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
|
||||
" goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
|
||||
" parameter value=" + parameterValue);
|
||||
goalValue = Double.MAX_VALUE;
|
||||
}
|
||||
goalValueCache.put(point, value);
|
||||
goalValueCache.put(point, goalValue);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double preComputed(Point point) {
|
||||
Double value = goalValueCache.get(point);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
log.verbose("Parameter value at point " + point + " is " + goalValue + ", goal function value=" + goalValue);
|
||||
|
||||
// TODO: : is in domain?
|
||||
return 0;
|
||||
return goalValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the parameter value at a point that has been computed. The purpose is
|
||||
* to allow retrieving the parameter value corresponding to the found minimum value.
|
||||
@ -123,13 +147,15 @@ public class RocketOptimizationFunction implements Function {
|
||||
/**
|
||||
* Returns a new deep copy of the simulation and rocket. This methods performs
|
||||
* synchronization on the simulation for thread protection.
|
||||
* <p>
|
||||
* Note: This method is package-private for unit testing purposes.
|
||||
*
|
||||
* @return
|
||||
* @return a new deep copy of the simulation and rocket
|
||||
*/
|
||||
private Simulation newSimulationInstance() {
|
||||
Simulation newSimulationInstance(Simulation simulation) {
|
||||
synchronized (baseSimulation) {
|
||||
Rocket newRocket = (Rocket) baseSimulation.getRocket().copy();
|
||||
Simulation newSimulation = baseSimulation.duplicateSimulation(newRocket);
|
||||
Rocket newRocket = (Rocket) simulation.getRocket().copy();
|
||||
Simulation newSimulation = simulation.duplicateSimulation(newRocket);
|
||||
return newSimulation;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package net.sf.openrocket.optimization.rocketoptimization;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
|
||||
/**
|
||||
* An interface defining a function domain which limits allowed function values.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public interface SimulationDomain {
|
||||
|
||||
/**
|
||||
* Return a value determining whether the simulation is within the domain limits
|
||||
* of an optimization process. If the returned value is negative or zero, the
|
||||
* simulation is within the domain; if the value is positive, the returned value
|
||||
* is an indication of how far from the domain the value is; if the returned value
|
||||
* is NaN, the simulation is outside of the domain.
|
||||
*
|
||||
* @param simulation the simulation to check.
|
||||
* @return a negative value or zero if the simulation is in the domain;
|
||||
* a positive value or NaN if not.
|
||||
*/
|
||||
public double getDistanceToDomain(Simulation simulation);
|
||||
|
||||
}
|
@ -72,7 +72,7 @@ public interface SimulationModifier extends ChangeSource {
|
||||
/**
|
||||
* Return the current scaled value. This is normally within the range [0...1], but
|
||||
* can be outside the range if the current value is outside of the min and max values.
|
||||
* @return
|
||||
* @return the current value of this parameter (normally between [0 ... 1])
|
||||
*/
|
||||
public double getCurrentScaledValue();
|
||||
|
||||
|
@ -0,0 +1,162 @@
|
||||
package net.sf.openrocket.optimization.rocketoptimization.modifiers;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import net.sf.openrocket.util.Reflection.Method;
|
||||
|
||||
public class GenericModifier implements SimulationModifier {
|
||||
|
||||
private final String name;
|
||||
private final Object relatedObject;
|
||||
private final UnitGroup unitGroup;
|
||||
private final double multiplier;
|
||||
private final Object modifiable;
|
||||
|
||||
private final Method getter;
|
||||
private final Method setter;
|
||||
|
||||
private double minValue;
|
||||
private double maxValue;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public GenericModifier(String modifierName, Object relatedObject, UnitGroup unitGroup, double multiplier,
|
||||
Object modifiable, String methodName) {
|
||||
this.name = modifierName;
|
||||
this.relatedObject = relatedObject;
|
||||
this.unitGroup = unitGroup;
|
||||
this.multiplier = multiplier;
|
||||
this.modifiable = modifiable;
|
||||
|
||||
if (MathUtil.equals(multiplier, 0)) {
|
||||
throw new IllegalArgumentException("multiplier is zero");
|
||||
}
|
||||
|
||||
try {
|
||||
methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
|
||||
getter = new Method(modifiable.getClass().getMethod("get" + methodName));
|
||||
setter = new Method(modifiable.getClass().getMethod("set" + methodName, double.class));
|
||||
} catch (SecurityException e) {
|
||||
throw new BugException("Trying to find method get/set" + methodName + " in class " + modifiable.getClass(), e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new BugException("Trying to find method get/set" + methodName + " in class " + modifiable.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRelatedObject() {
|
||||
return relatedObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCurrentValue() {
|
||||
return ((Double) getter.invoke(modifiable)) * multiplier;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double getCurrentScaledValue() {
|
||||
double value = getCurrentValue();
|
||||
return toScaledValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modify(Simulation simulation, double scaledValue) {
|
||||
double siValue = toBaseValue(scaledValue) / multiplier;
|
||||
setter.invoke(modifiable, siValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the scaled value (normally within [0...1]).
|
||||
*/
|
||||
private double toScaledValue(double value) {
|
||||
if (MathUtil.equals(minValue, maxValue)) {
|
||||
if (value > maxValue)
|
||||
return 1.0;
|
||||
if (value < minValue)
|
||||
return 0.0;
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
return MathUtil.map(value, minValue, maxValue, 0.0, 1.0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base value (in SI units).
|
||||
*/
|
||||
private double toBaseValue(double value) {
|
||||
return MathUtil.map(value, 0.0, 1.0, minValue, maxValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public double getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinValue(double value) {
|
||||
if (MathUtil.equals(minValue, value))
|
||||
return;
|
||||
this.minValue = value;
|
||||
if (maxValue < minValue)
|
||||
maxValue = minValue;
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxValue(double value) {
|
||||
if (MathUtil.equals(maxValue, value))
|
||||
return;
|
||||
this.maxValue = value;
|
||||
if (minValue > maxValue)
|
||||
minValue = maxValue;
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitGroup getUnitGroup() {
|
||||
return unitGroup;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addChangeListener(ChangeListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChangeListener(ChangeListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void fireChangeEvent() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.sf.openrocket.optimization.rocketoptimization.parameters;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener;
|
||||
|
||||
/**
|
||||
* An optimization parameter that computes the maximum altitude of a simulated flight.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class MaximumAltitudeParameter implements OptimizableParameter {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Maximum altitude";
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeValue(Simulation simulation) throws OptimizationException {
|
||||
try {
|
||||
simulation.simulate(new ApogeeEndListener());
|
||||
return simulation.getSimulatedData().getBranch(0).getMaximum(FlightDataType.TYPE_ALTITUDE);
|
||||
} catch (SimulationException e) {
|
||||
throw new OptimizationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -12,7 +12,10 @@ import java.util.UUID;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.EventListenerList;
|
||||
|
||||
import net.sf.openrocket.gui.main.ExceptionHandler;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.motor.Motor;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.Chars;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
@ -30,9 +33,9 @@ import net.sf.openrocket.util.UniqueID;
|
||||
*/
|
||||
|
||||
public class Rocket extends RocketComponent {
|
||||
public static final double DEFAULT_REFERENCE_LENGTH = 0.01;
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
private static final boolean DEBUG_LISTENERS = false;
|
||||
public static final double DEFAULT_REFERENCE_LENGTH = 0.01;
|
||||
|
||||
|
||||
/**
|
||||
@ -343,17 +346,15 @@ public class Rocket extends RocketComponent {
|
||||
public void addComponentChangeListener(ComponentChangeListener l) {
|
||||
checkState();
|
||||
listenerList.add(ComponentChangeListener.class, l);
|
||||
if (DEBUG_LISTENERS)
|
||||
System.out.println(this + ": Added listner (now " + listenerList.getListenerCount() +
|
||||
" listeners): " + l);
|
||||
log.verbose("Added ComponentChangeListener " + l + ", current number of listeners is " +
|
||||
listenerList.getListenerCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeComponentChangeListener(ComponentChangeListener l) {
|
||||
listenerList.remove(ComponentChangeListener.class, l);
|
||||
if (DEBUG_LISTENERS)
|
||||
System.out.println(this + ": Removed listner (now " + listenerList.getListenerCount() +
|
||||
" listeners): " + l);
|
||||
log.verbose("Removed ComponentChangeListener " + l + ", current number of listeners is " +
|
||||
listenerList.getListenerCount());
|
||||
}
|
||||
|
||||
|
||||
@ -361,17 +362,15 @@ public class Rocket extends RocketComponent {
|
||||
public void addChangeListener(ChangeListener l) {
|
||||
checkState();
|
||||
listenerList.add(ChangeListener.class, l);
|
||||
if (DEBUG_LISTENERS)
|
||||
System.out.println(this + ": Added listner (now " + listenerList.getListenerCount() +
|
||||
" listeners): " + l);
|
||||
log.verbose("Added ChangeListener " + l + ", current number of listeners is " +
|
||||
listenerList.getListenerCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeChangeListener(ChangeListener l) {
|
||||
listenerList.remove(ChangeListener.class, l);
|
||||
if (DEBUG_LISTENERS)
|
||||
System.out.println(this + ": Removed listner (now " + listenerList.getListenerCount() +
|
||||
" listeners): " + l);
|
||||
log.verbose("Removed ChangeListener " + l + ", current number of listeners is " +
|
||||
listenerList.getListenerCount());
|
||||
}
|
||||
|
||||
|
||||
@ -392,15 +391,15 @@ public class Rocket extends RocketComponent {
|
||||
functionalModID = modID;
|
||||
}
|
||||
|
||||
if (DEBUG_LISTENERS)
|
||||
System.out.println("FIRING " + e);
|
||||
|
||||
// Check whether frozen
|
||||
if (freezeList != null) {
|
||||
log.debug("Rocket is in frozen state, adding event " + e + " info freeze list");
|
||||
freezeList.add(e);
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Firing rocket change event " + e);
|
||||
|
||||
// Notify all components first
|
||||
Iterator<RocketComponent> iterator = this.deepIterator(true);
|
||||
while (iterator.hasNext()) {
|
||||
@ -441,6 +440,10 @@ public class Rocket extends RocketComponent {
|
||||
checkState();
|
||||
if (freezeList == null) {
|
||||
freezeList = new LinkedList<ComponentChangeEvent>();
|
||||
log.debug("Freezing Rocket");
|
||||
} else {
|
||||
ExceptionHandler.handleErrorCondition("Attempting to freeze Rocket when it is already frozen, " +
|
||||
"freezeList=" + freezeList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,13 +456,18 @@ public class Rocket extends RocketComponent {
|
||||
*/
|
||||
public void thaw() {
|
||||
checkState();
|
||||
if (freezeList == null)
|
||||
if (freezeList == null) {
|
||||
ExceptionHandler.handleErrorCondition("Attempting to thaw Rocket when it is not frozen");
|
||||
return;
|
||||
}
|
||||
if (freezeList.size() == 0) {
|
||||
log.warn("Thawing rocket with no changes made");
|
||||
freezeList = null;
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Thawing rocket, freezeList=" + freezeList);
|
||||
|
||||
int type = 0;
|
||||
Object c = null;
|
||||
for (ComponentChangeEvent e : freezeList) {
|
||||
|
@ -12,7 +12,9 @@ import java.util.Stack;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.logging.TraceException;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.util.BugException;
|
||||
import net.sf.openrocket.util.ChangeSource;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -23,6 +25,7 @@ import net.sf.openrocket.util.UniqueID;
|
||||
|
||||
public abstract class RocketComponent implements ChangeSource, Cloneable,
|
||||
Iterable<RocketComponent> {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
/*
|
||||
* Text is suitable to the form
|
||||
@ -1304,6 +1307,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
|
||||
checkState();
|
||||
if (parent == null) {
|
||||
/* Ignore if root invalid. */
|
||||
log.debug("Attempted firing event " + e + " with root " + this.getComponentName() + ", ignoring event");
|
||||
return;
|
||||
}
|
||||
getRoot().fireComponentChangeEvent(e);
|
||||
|
@ -41,6 +41,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
private SimulationStatus status;
|
||||
|
||||
|
||||
@Override
|
||||
public FlightData simulate(SimulationConditions simulationConditions) throws SimulationException {
|
||||
Set<MotorId> motorBurntOut = new HashSet<MotorId>();
|
||||
|
||||
@ -409,7 +410,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
case STAGE_SEPARATION: {
|
||||
// TODO: HIGH: Store lower stages to be simulated later
|
||||
RocketComponent stage = (RocketComponent) event.getSource();
|
||||
RocketComponent stage = event.getSource();
|
||||
int n = stage.getStageNumber();
|
||||
status.getConfiguration().setToStage(n);
|
||||
status.getFlightData().addEvent(event);
|
||||
@ -542,6 +543,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
d += status.getEffectiveLaunchRodLength();
|
||||
|
||||
if (Double.isNaN(d) || b) {
|
||||
log.error("Simulation resulted in NaN value:" +
|
||||
" simulationTime=" + status.getSimulationTime() +
|
||||
" previousTimeStep=" + status.getPreviousTimeStep() +
|
||||
" rocketPosition=" + status.getRocketPosition() +
|
||||
" rocketVelocity=" + status.getRocketVelocity() +
|
||||
" rocketOrientationQuaternion=" + status.getRocketOrientationQuaternion() +
|
||||
" rocketRotationVelocity=" + status.getRocketRotationVelocity() +
|
||||
" effectiveLaunchRodLength=" + status.getEffectiveLaunchRodLength());
|
||||
throw new SimulationException("Simulation resulted in not-a-number (NaN) value, please report a bug.");
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,9 @@ public class Startup {
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
|
||||
// Check for "openrocket.debug" property before anything else
|
||||
checkDebugStatus();
|
||||
|
||||
// Initialize logging first so we can use it
|
||||
initializeLogging();
|
||||
|
||||
@ -91,6 +94,18 @@ public class Startup {
|
||||
|
||||
|
||||
|
||||
private static void checkDebugStatus() {
|
||||
if (System.getProperty("openrocket.debug") != null) {
|
||||
System.setProperty("openrocket.log.stdout", "VBOSE");
|
||||
System.setProperty("openrocket.log.tracelevel", "VBOSE");
|
||||
System.setProperty("openrocket.debug.menu", "true");
|
||||
System.setProperty("openrocket.debug.motordigest", "true");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static void runMain(String[] args) {
|
||||
|
||||
// Initialize the splash screen with version info
|
||||
|
26
src/net/sf/openrocket/util/ConcurrencyException.java
Normal file
26
src/net/sf/openrocket/util/ConcurrencyException.java
Normal file
@ -0,0 +1,26 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
/**
|
||||
* An exception that indicates a concurrency bug in the software.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class ConcurrencyException extends FatalException {
|
||||
|
||||
public ConcurrencyException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ConcurrencyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConcurrencyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConcurrencyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
@ -128,6 +128,7 @@ public class GUIUtil {
|
||||
*/
|
||||
public static void installEscapeCloseOperation(final JDialog dialog) {
|
||||
Action dispatchClosing = new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
log.user("Closing dialog " + dialog);
|
||||
dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING));
|
||||
@ -194,6 +195,7 @@ public class GUIUtil {
|
||||
@Override
|
||||
public void windowClosed(WindowEvent e) {
|
||||
setNullModels(window);
|
||||
MemoryManagement.collectable(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -341,7 +343,7 @@ public class GUIUtil {
|
||||
|
||||
JTree tree = (JTree) c;
|
||||
tree.setModel(new DefaultTreeModel(new TreeNode() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public Enumeration children() {
|
||||
return new Vector().elements();
|
||||
|
137
src/net/sf/openrocket/util/MemoryManagement.java
Normal file
137
src/net/sf/openrocket/util/MemoryManagement.java
Normal file
@ -0,0 +1,137 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
/**
|
||||
* A class that performs certain memory-management operations for debugging purposes.
|
||||
* For example, complex objects that are being discarded and that should be garbage-collectable
|
||||
* (such as dialog windows) should be registered for monitoring by calling
|
||||
* {@link #collectable(Object)}. This will allow monitoring whether the object really is
|
||||
* garbage-collected or whether it is retained in memory due to a memory leak.
|
||||
* Only complex objects should be registered due to the overhead of the monitoring.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public final class MemoryManagement {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
/** Purge cleared references every this many calls to {@link #collectable(Object)} */
|
||||
private static final int PURGE_CALL_COUNT = 100;
|
||||
|
||||
|
||||
/**
|
||||
* Storage of the objects. This is basically a mapping from the objects (using weak references)
|
||||
* to
|
||||
*/
|
||||
private static List<MemoryData> objects = new LinkedList<MemoryData>();
|
||||
private static int callCount = 0;
|
||||
|
||||
|
||||
private MemoryManagement() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark an object that should be garbage-collectable by the GC. This class will monitor
|
||||
* whether the object actually gets garbage-collected or not by holding a weak reference
|
||||
* to the object.
|
||||
*
|
||||
* @param o the object to monitor.
|
||||
*/
|
||||
public static synchronized void collectable(Object o) {
|
||||
if (o == null) {
|
||||
throw new IllegalArgumentException("object is null");
|
||||
}
|
||||
log.debug("Adding object into collectable list: " + o);
|
||||
objects.add(new MemoryData(o));
|
||||
callCount++;
|
||||
if (callCount % PURGE_CALL_COUNT == 0) {
|
||||
purge();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of times {@link #collectable(Object)} has been called.
|
||||
* @return the number of times {@link #collectable(Object)} has been called.
|
||||
*/
|
||||
public static synchronized int getCallCount() {
|
||||
return callCount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a list of MemoryData objects corresponding to the objects that have been
|
||||
* registered by {@link #collectable(Object)} and have not been garbage-collected properly.
|
||||
* This method first calls <code>System.gc()</code> multiple times to attempt to
|
||||
* force any remaining garbage collection.
|
||||
*
|
||||
* @return a list of MemoryData objects for objects that have not yet been garbage-collected.
|
||||
*/
|
||||
public static synchronized ArrayList<MemoryData> getRemainingObjects() {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
System.runFinalization();
|
||||
System.gc();
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
purge();
|
||||
return new ArrayList<MemoryData>(objects);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Purge all cleared references from the object list.
|
||||
*/
|
||||
private static void purge() {
|
||||
int origCount = objects.size();
|
||||
Iterator<MemoryData> iterator = objects.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MemoryData data = iterator.next();
|
||||
if (data.getReference().get() == null) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
log.debug(objects.size() + " of " + origCount + " objects remaining in discarded objects list after purge.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A value object class containing data of a discarded object reference.
|
||||
*/
|
||||
public static final class MemoryData {
|
||||
private final WeakReference<Object> reference;
|
||||
private final long registrationTime;
|
||||
|
||||
private MemoryData(Object object) {
|
||||
this.reference = new WeakReference<Object>(object);
|
||||
this.registrationTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the weak reference to the discarded object.
|
||||
*/
|
||||
public WeakReference<Object> getReference() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time when the object was discarded.
|
||||
* @return a millisecond timestamp of when the object was discarded.
|
||||
*/
|
||||
public long getRegistrationTime() {
|
||||
return registrationTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
171
src/net/sf/openrocket/util/SafetyMutex.java
Normal file
171
src/net/sf/openrocket/util/SafetyMutex.java
Normal file
@ -0,0 +1,171 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
import net.sf.openrocket.gui.main.ExceptionHandler;
|
||||
import net.sf.openrocket.logging.LogHelper;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
/**
|
||||
* A mutex that can be used for verifying thread safety. This class cannot be
|
||||
* used to perform synchronization, only to detect concurrency issues. This
|
||||
* class can be used by the main methods of non-thread-safe classes to ensure
|
||||
* the class is not wrongly used from multiple threads concurrently.
|
||||
* <p>
|
||||
* This mutex is not reentrant even for the same thread that has locked it.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class SafetyMutex {
|
||||
private static final LogHelper log = Application.getLogger();
|
||||
|
||||
// Package-private for unit testing
|
||||
static volatile boolean errorReported = false;
|
||||
|
||||
// lockingThread is set when this mutex is locked.
|
||||
Thread lockingThread = null;
|
||||
// Stack of places that have locked this mutex
|
||||
final LinkedList<String> locations = new LinkedList<String>();
|
||||
|
||||
|
||||
/**
|
||||
* Verify that this mutex is unlocked, but don't lock it. This has the same effect
|
||||
* as <code>mutex.lock(); mutex.unlock();</code> and is useful for methods that return
|
||||
* immediately (e.g. getters).
|
||||
*
|
||||
* @throws ConcurrencyException if this mutex is already locked.
|
||||
*/
|
||||
public synchronized void verify() {
|
||||
checkState(true);
|
||||
if (lockingThread != null && lockingThread != Thread.currentThread()) {
|
||||
error("Mutex is already locked", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lock this mutex. If this mutex is already locked an error is raised and
|
||||
* a ConcurrencyException is thrown. The location parameter is used to distinguish
|
||||
* the locking location, and it should be e.g. the method name.
|
||||
*
|
||||
* @param location a string describing the location where this mutex was locked (cannot be null).
|
||||
*
|
||||
* @throws ConcurrencyException if this mutex is already locked.
|
||||
*/
|
||||
public synchronized void lock(String location) {
|
||||
if (location == null) {
|
||||
throw new IllegalArgumentException("location is null");
|
||||
}
|
||||
checkState(true);
|
||||
|
||||
Thread currentThread = Thread.currentThread();
|
||||
if (lockingThread != null && lockingThread != currentThread) {
|
||||
error("Mutex is already locked", true);
|
||||
}
|
||||
|
||||
lockingThread = currentThread;
|
||||
locations.push(location);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Unlock this mutex. If this mutex is not locked at the position of the parameter
|
||||
* or was locked by another thread than the current thread an error is raised,
|
||||
* but an exception is not thrown.
|
||||
* <p>
|
||||
* This method is guaranteed never to throw an exception, so it can safely be used in finally blocks.
|
||||
*
|
||||
* @param location a location string matching that which locked the mutex
|
||||
* @return whether the unlocking was successful (this normally doesn't need to be checked)
|
||||
*/
|
||||
public synchronized boolean unlock(String location) {
|
||||
try {
|
||||
|
||||
if (location == null) {
|
||||
ExceptionHandler.handleErrorCondition("location is null");
|
||||
location = "";
|
||||
}
|
||||
checkState(false);
|
||||
|
||||
|
||||
// Check that the mutex is locked
|
||||
if (lockingThread == null) {
|
||||
error("Mutex was not locked", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the mutex is locked by the current thread
|
||||
if (lockingThread != Thread.currentThread()) {
|
||||
error("Mutex is being unlocked from differerent thread than where it was locked", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the unlock location is correct
|
||||
String lastLocation = locations.pop();
|
||||
if (!location.equals(lastLocation)) {
|
||||
locations.push(lastLocation);
|
||||
error("Mutex unlocking location does not match locking location, location=" + location, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unlock the mutex if the last one
|
||||
if (locations.isEmpty()) {
|
||||
lockingThread = null;
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
ExceptionHandler.handleErrorCondition("An exception occurred while unlocking a mutex, " +
|
||||
"locking thread=" + lockingThread + " locations=" + locations, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the internal state of the mutex (lockingThread vs. locations) is correct.
|
||||
*/
|
||||
private void checkState(boolean throwException) {
|
||||
/*
|
||||
* Disallowed states:
|
||||
* lockingThread == null && !locations.isEmpty()
|
||||
* lockingThread != null && locations.isEmpty()
|
||||
*/
|
||||
if ((lockingThread == null) ^ (locations.isEmpty())) {
|
||||
// Clear the mutex only after error() has executed (and possibly thrown an exception)
|
||||
try {
|
||||
error("Mutex data inconsistency occurred - unlocking mutex", throwException);
|
||||
} finally {
|
||||
lockingThread = null;
|
||||
locations.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Raise an error. The first occurrence is passed directly to the exception handler,
|
||||
* later errors are simply logged.
|
||||
*/
|
||||
private void error(String message, boolean throwException) {
|
||||
message = message +
|
||||
", current thread = " + Thread.currentThread() +
|
||||
", locking thread=" + lockingThread +
|
||||
", locking locations=" + locations;
|
||||
|
||||
ConcurrencyException ex = new ConcurrencyException(message);
|
||||
|
||||
if (!errorReported) {
|
||||
errorReported = true;
|
||||
ExceptionHandler.handleErrorCondition(ex);
|
||||
} else {
|
||||
log.error(message, ex);
|
||||
}
|
||||
|
||||
if (throwException) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package net.sf.openrocket.utils;
|
||||
import net.sf.openrocket.optimization.general.Function;
|
||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.ParallelExecutorCache;
|
||||
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
@ -22,7 +23,7 @@ public class TestFunctionOptimizer {
|
||||
|
||||
|
||||
private void go(final ParallelFunctionCache functionCache,
|
||||
final FunctionOptimizer optimizer, final Point optimum, final int maxSteps) {
|
||||
final FunctionOptimizer optimizer, final Point optimum, final int maxSteps) throws OptimizationException {
|
||||
|
||||
Function function = new Function() {
|
||||
@Override
|
||||
@ -35,15 +36,6 @@ public class TestFunctionOptimizer {
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double preComputed(Point p) {
|
||||
for (double d : p.asArray()) {
|
||||
if (d < 0 || d > 1)
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
return Double.NaN;
|
||||
}
|
||||
};
|
||||
|
||||
OptimizationController control = new OptimizationController() {
|
||||
@ -83,7 +75,7 @@ public class TestFunctionOptimizer {
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
public static void main(String[] args) throws InterruptedException, OptimizationException {
|
||||
|
||||
System.err.println("Number of processors: " + Runtime.getRuntime().availableProcessors());
|
||||
|
||||
|
@ -10,6 +10,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import net.sf.openrocket.optimization.general.Function;
|
||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.ParallelExecutorCache;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.optimization.general.multidim.MultidirectionalSearchOptimizer;
|
||||
@ -26,7 +27,7 @@ public class TestFunctionOptimizerLoop {
|
||||
|
||||
|
||||
|
||||
private void go(final FunctionOptimizer optimizer, final Point optimum, final int maxSteps, ExecutorService executor) {
|
||||
private void go(final FunctionOptimizer optimizer, final Point optimum, final int maxSteps, ExecutorService executor) throws OptimizationException {
|
||||
|
||||
Function function = new Function() {
|
||||
@Override
|
||||
@ -34,15 +35,6 @@ public class TestFunctionOptimizerLoop {
|
||||
evaluations++;
|
||||
return p.sub(optimum).length2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double preComputed(Point p) {
|
||||
for (double d : p.asArray()) {
|
||||
if (d < 0 || d > 1)
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
return Double.NaN;
|
||||
}
|
||||
};
|
||||
|
||||
OptimizationController control = new OptimizationController() {
|
||||
@ -67,7 +59,7 @@ public class TestFunctionOptimizerLoop {
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws OptimizationException {
|
||||
|
||||
System.err.println("PRECISION = " + PRECISION);
|
||||
|
||||
|
@ -0,0 +1,214 @@
|
||||
package net.sf.openrocket.optimization.rocketoptimization;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||
import net.sf.openrocket.optimization.general.Point;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.auto.Mock;
|
||||
import org.jmock.integration.junit4.JMock;
|
||||
import org.jmock.integration.junit4.JUnit4Mockery;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
||||
@RunWith(JMock.class)
|
||||
public class TestRocketOptimizationFunction {
|
||||
Mockery context = new JUnit4Mockery();
|
||||
|
||||
@Mock
|
||||
OptimizableParameter parameter;
|
||||
@Mock
|
||||
OptimizationGoal goal;
|
||||
@Mock
|
||||
SimulationDomain domain;
|
||||
@Mock
|
||||
SimulationModifier modifier1;
|
||||
@Mock
|
||||
SimulationModifier modifier2;
|
||||
|
||||
|
||||
@Test
|
||||
public void testNormalEvaluation() throws InterruptedException, OptimizationException {
|
||||
final Rocket rocket = new Rocket();
|
||||
final Simulation simulation = new Simulation(rocket);
|
||||
|
||||
final double p1 = 0.4;
|
||||
final double p2 = 0.7;
|
||||
final double ddist = -0.43;
|
||||
final double pvalue = 9.81;
|
||||
final double gvalue = 8.81;
|
||||
|
||||
// @formatter:off
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(modifier1).modify(simulation, p1);
|
||||
oneOf(modifier2).modify(simulation, p2);
|
||||
oneOf(domain).getDistanceToDomain(simulation); will(returnValue(ddist));
|
||||
oneOf(parameter).computeValue(simulation); will(returnValue(pvalue));
|
||||
oneOf(goal).getMinimizationParameter(pvalue); will(returnValue(gvalue));
|
||||
}});
|
||||
// @formatter:on
|
||||
|
||||
|
||||
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,
|
||||
parameter, goal, domain, modifier1, modifier2) {
|
||||
@Override
|
||||
Simulation newSimulationInstance(Simulation sim) {
|
||||
return sim;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
double value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(gvalue, value, 0);
|
||||
|
||||
assertEquals(pvalue, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
// Re-evaluate the point to verify parameter is not recomputed
|
||||
value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(gvalue, value, 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNaNValue() throws InterruptedException, OptimizationException {
|
||||
final Rocket rocket = new Rocket();
|
||||
final Simulation simulation = new Simulation(rocket);
|
||||
|
||||
final double p1 = 0.4;
|
||||
final double p2 = 0.7;
|
||||
final double ddist = -0.43;
|
||||
final double pvalue = 9.81;
|
||||
|
||||
// @formatter:off
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(modifier1).modify(simulation, p1);
|
||||
oneOf(modifier2).modify(simulation, p2);
|
||||
oneOf(domain).getDistanceToDomain(simulation); will(returnValue(ddist));
|
||||
oneOf(parameter).computeValue(simulation); will(returnValue(pvalue));
|
||||
oneOf(goal).getMinimizationParameter(pvalue); will(returnValue(Double.NaN));
|
||||
}});
|
||||
// @formatter:on
|
||||
|
||||
|
||||
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,
|
||||
parameter, goal, domain, modifier1, modifier2) {
|
||||
@Override
|
||||
Simulation newSimulationInstance(Simulation sim) {
|
||||
return sim;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
double value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(Double.MAX_VALUE, value, 0);
|
||||
|
||||
assertEquals(pvalue, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(Double.MAX_VALUE, value, 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOutsideDomain() throws InterruptedException, OptimizationException {
|
||||
final Rocket rocket = new Rocket();
|
||||
final Simulation simulation = new Simulation(rocket);
|
||||
|
||||
final double p1 = 0.4;
|
||||
final double p2 = 0.7;
|
||||
final double ddist = 0.98;
|
||||
|
||||
// @formatter:off
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(modifier1).modify(simulation, p1);
|
||||
oneOf(modifier2).modify(simulation, p2);
|
||||
oneOf(domain).getDistanceToDomain(simulation); will(returnValue(ddist));
|
||||
}});
|
||||
// @formatter:on
|
||||
|
||||
|
||||
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,
|
||||
parameter, goal, domain, modifier1, modifier2) {
|
||||
@Override
|
||||
Simulation newSimulationInstance(Simulation sim) {
|
||||
return sim;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
double value = function.evaluate(new Point(p1, p2));
|
||||
assertTrue(value > 1e100);
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
value = function.evaluate(new Point(p1, p2));
|
||||
assertTrue(value > 1e100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutsideDomain2() throws InterruptedException, OptimizationException {
|
||||
final Rocket rocket = new Rocket();
|
||||
final Simulation simulation = new Simulation(rocket);
|
||||
|
||||
final double p1 = 0.4;
|
||||
final double p2 = 0.7;
|
||||
final double ddist = Double.NaN;
|
||||
|
||||
// @formatter:off
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(modifier1).modify(simulation, p1);
|
||||
oneOf(modifier2).modify(simulation, p2);
|
||||
oneOf(domain).getDistanceToDomain(simulation); will(returnValue(ddist));
|
||||
}});
|
||||
// @formatter:on
|
||||
|
||||
|
||||
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,
|
||||
parameter, goal, domain, modifier1, modifier2) {
|
||||
@Override
|
||||
Simulation newSimulationInstance(Simulation sim) {
|
||||
return sim;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
double value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(Double.MAX_VALUE, value, 0);
|
||||
|
||||
assertEquals(Double.NaN, function.getComputedParameterValue(new Point(p1, p2)), 0);
|
||||
|
||||
value = function.evaluate(new Point(p1, p2));
|
||||
assertEquals(Double.MAX_VALUE, value, 0);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNewSimulationInstance() {
|
||||
final Rocket rocket = new Rocket();
|
||||
rocket.setName("Foobar");
|
||||
final Simulation simulation = new Simulation(rocket);
|
||||
simulation.setName("MySim");
|
||||
|
||||
RocketOptimizationFunction function = new RocketOptimizationFunction(simulation,
|
||||
parameter, goal, domain, modifier1, modifier2);
|
||||
|
||||
Simulation sim = function.newSimulationInstance(simulation);
|
||||
assertFalse(simulation == sim);
|
||||
assertEquals("MySim", sim.getName());
|
||||
assertFalse(rocket == sim.getRocket());
|
||||
assertEquals("Foobar", sim.getRocket().getName());
|
||||
}
|
||||
|
||||
}
|
156
test/net/sf/openrocket/util/TestMutex.java
Normal file
156
test/net/sf/openrocket/util/TestMutex.java
Normal file
@ -0,0 +1,156 @@
|
||||
package net.sf.openrocket.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestMutex {
|
||||
|
||||
@Test
|
||||
public void testSingleLocking() {
|
||||
SafetyMutex m = new SafetyMutex();
|
||||
|
||||
// Test single locking
|
||||
assertNull(m.lockingThread);
|
||||
m.verify();
|
||||
m.lock("here");
|
||||
assertNotNull(m.lockingThread);
|
||||
assertTrue(m.unlock("here"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleLocking() {
|
||||
SafetyMutex m = new SafetyMutex();
|
||||
|
||||
// Test double locking
|
||||
m.verify();
|
||||
m.lock("foobar");
|
||||
m.verify();
|
||||
m.lock("bazqux");
|
||||
m.verify();
|
||||
assertTrue(m.unlock("bazqux"));
|
||||
m.verify();
|
||||
assertTrue(m.unlock("foobar"));
|
||||
m.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleUnlocking() {
|
||||
SafetyMutex m = new SafetyMutex();
|
||||
// Mark error reported to not init exception handler
|
||||
SafetyMutex.errorReported = true;
|
||||
|
||||
m.lock("here");
|
||||
assertTrue(m.unlock("here"));
|
||||
assertFalse(m.unlock("here"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private volatile int testState = 0;
|
||||
private volatile String failure = null;
|
||||
|
||||
@Test(timeout = 1000)
|
||||
public void testThreadingErrors() {
|
||||
final SafetyMutex m = new SafetyMutex();
|
||||
|
||||
// Initialize and start the thread
|
||||
Thread thread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
// Test locking a locked mutex
|
||||
waitFor(1);
|
||||
try {
|
||||
m.lock("in thread one");
|
||||
failure = "Succeeded in locking a mutex locked by a different thread";
|
||||
return;
|
||||
} catch (ConcurrencyException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
// Test unlocking a mutex locked by a different thread
|
||||
if (m.unlock("in thread two")) {
|
||||
failure = "Succeeded in unlocking a mutex locked by a different thread";
|
||||
return;
|
||||
}
|
||||
|
||||
// Test verifying a locked mutex that already has an error
|
||||
try {
|
||||
m.verify();
|
||||
failure = "Succeeded in verifying a mutex locked by a different thread";
|
||||
return;
|
||||
} catch (ConcurrencyException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
// Test locking a mutex after it's been unlocked
|
||||
testState = 2;
|
||||
waitFor(3);
|
||||
m.lock("in thread three");
|
||||
m.verify();
|
||||
|
||||
// Wait for other side to test
|
||||
testState = 4;
|
||||
waitFor(5);
|
||||
|
||||
// Exit code
|
||||
testState = 6;
|
||||
|
||||
} catch (Exception e) {
|
||||
failure = "Exception occurred in thread: " + e;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
m.lock("one");
|
||||
testState = 1;
|
||||
|
||||
waitFor(2);
|
||||
assertNull("Thread error: " + failure, failure);
|
||||
|
||||
m.verify();
|
||||
m.unlock("one");
|
||||
testState = 3;
|
||||
|
||||
waitFor(4);
|
||||
assertNull("Thread error: " + failure, failure);
|
||||
|
||||
try {
|
||||
m.lock("two");
|
||||
fail("Succeeded in locking a locked mutex in main thread");
|
||||
} catch (ConcurrencyException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
// Test unlocking a mutex locked by a different thread
|
||||
assertFalse(m.unlock("here"));
|
||||
|
||||
try {
|
||||
m.verify();
|
||||
fail("Succeeded in verifying a locked mutex in main thread");
|
||||
} catch (ConcurrencyException e) {
|
||||
// OK
|
||||
}
|
||||
|
||||
testState = 5;
|
||||
waitFor(6);
|
||||
assertNull("Thread error: " + failure, failure);
|
||||
}
|
||||
|
||||
private void waitFor(int state) {
|
||||
while (testState != state && failure == null) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user