SafetyMutex and rocket optimization updates
This commit is contained in:
parent
833e2f80cf
commit
a63f0b4bae
@ -10,7 +10,6 @@
|
|||||||
</accessrules>
|
</accessrules>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/>
|
<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/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">
|
<classpathentry kind="lib" path="lib/jcommon-1.0.16.jar">
|
||||||
<accessrules>
|
<accessrules>
|
||||||
@ -18,5 +17,10 @@
|
|||||||
</accessrules>
|
</accessrules>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="lib" path="lib/miglayout15-swing.jar"/>
|
<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"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</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
|
2010-10-09 Sampo Niskanen
|
||||||
|
|
||||||
* [BUG] Fixed conversion to freeform fin set
|
* [BUG] Fixed conversion to freeform fin set
|
||||||
|
@ -36,8 +36,11 @@
|
|||||||
<pathelement location="${build-test.dir}"/>
|
<pathelement location="${build-test.dir}"/>
|
||||||
<pathelement location="${classes.dir}"/>
|
<pathelement location="${classes.dir}"/>
|
||||||
<pathelement location="${src-test.dir}"/>
|
<pathelement location="${src-test.dir}"/>
|
||||||
<!-- <pathelement location="${ant.library.dir}/junit4.jar"/> -->
|
<pathelement location="lib-test/junit-dep-4.8.2.jar"/>
|
||||||
<pathelement location="lib-test/junit-4.7.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>
|
</path>
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +25,14 @@ openrocket.log.tracelevel
|
|||||||
Debugging options
|
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
|
openrocket.debug.menu
|
||||||
If defined the "Debug" menu will be displayed in the main application window.
|
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.startup.Application;
|
||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
import net.sf.openrocket.util.ChangeSource;
|
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 {
|
public class Simulation implements ChangeSource, Cloneable {
|
||||||
private static final LogHelper log = Application.getLogger();
|
private static final LogHelper log = Application.getLogger();
|
||||||
|
|
||||||
@ -49,6 +57,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
NOT_SIMULATED
|
NOT_SIMULATED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final SafetyMutex mutex = new SafetyMutex();
|
||||||
|
|
||||||
private final Rocket rocket;
|
private final Rocket rocket;
|
||||||
|
|
||||||
@ -146,6 +155,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return the rocket.
|
* @return the rocket.
|
||||||
*/
|
*/
|
||||||
public Rocket getRocket() {
|
public Rocket getRocket() {
|
||||||
|
mutex.verify();
|
||||||
return rocket;
|
return rocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +167,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return a newly created Configuration of the launch conditions.
|
* @return a newly created Configuration of the launch conditions.
|
||||||
*/
|
*/
|
||||||
public Configuration getConfiguration() {
|
public Configuration getConfiguration() {
|
||||||
|
mutex.verify();
|
||||||
Configuration c = new Configuration(rocket);
|
Configuration c = new Configuration(rocket);
|
||||||
c.setMotorConfigurationID(conditions.getMotorConfigurationID());
|
c.setMotorConfigurationID(conditions.getMotorConfigurationID());
|
||||||
c.setAllStages();
|
c.setAllStages();
|
||||||
@ -171,6 +182,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return the simulation conditions.
|
* @return the simulation conditions.
|
||||||
*/
|
*/
|
||||||
public GUISimulationConditions getConditions() {
|
public GUISimulationConditions getConditions() {
|
||||||
|
mutex.verify();
|
||||||
return conditions;
|
return conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,6 +194,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return the actual list of simulation listeners.
|
* @return the actual list of simulation listeners.
|
||||||
*/
|
*/
|
||||||
public List<String> getSimulationListeners() {
|
public List<String> getSimulationListeners() {
|
||||||
|
mutex.verify();
|
||||||
return simulationListeners;
|
return simulationListeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +205,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return the name for the simulation.
|
* @return the name for the simulation.
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
mutex.verify();
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,15 +216,20 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @param name the name of the simulation.
|
* @param name the name of the simulation.
|
||||||
*/
|
*/
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
if (this.name.equals(name))
|
mutex.lock("setName");
|
||||||
return;
|
try {
|
||||||
|
if (this.name.equals(name))
|
||||||
|
return;
|
||||||
|
|
||||||
if (name == null)
|
if (name == null)
|
||||||
this.name = "";
|
this.name = "";
|
||||||
else
|
else
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
fireChangeEvent();
|
fireChangeEvent();
|
||||||
|
} finally {
|
||||||
|
mutex.unlock("setName");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -222,6 +241,8 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @see Status
|
* @see Status
|
||||||
*/
|
*/
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
|
mutex.verify();
|
||||||
|
|
||||||
if (status == Status.UPTODATE || status == Status.LOADED) {
|
if (status == Status.UPTODATE || status == Status.LOADED) {
|
||||||
if (rocket.getFunctionalModID() != simulatedRocketID ||
|
if (rocket.getFunctionalModID() != simulatedRocketID ||
|
||||||
!conditions.equals(simulatedConditions))
|
!conditions.equals(simulatedConditions))
|
||||||
@ -236,54 +257,59 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
|
|
||||||
public void simulate(SimulationListener... additionalListeners)
|
public void simulate(SimulationListener... additionalListeners)
|
||||||
throws SimulationException {
|
throws SimulationException {
|
||||||
|
mutex.lock("simulate");
|
||||||
if (this.status == Status.EXTERNAL) {
|
|
||||||
throw new SimulationException("Cannot simulate imported simulation.");
|
|
||||||
}
|
|
||||||
|
|
||||||
SimulationEngine simulator;
|
|
||||||
|
|
||||||
try {
|
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();
|
if (this.status == Status.EXTERNAL) {
|
||||||
for (SimulationListener l : additionalListeners) {
|
throw new SimulationException("Cannot simulate imported simulation.");
|
||||||
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);
|
|
||||||
|
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>.
|
* @return the conditions used in the previous simulation, or <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public GUISimulationConditions getSimulatedConditions() {
|
public GUISimulationConditions getSimulatedConditions() {
|
||||||
|
mutex.verify();
|
||||||
return simulatedConditions;
|
return simulatedConditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +333,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @see FlightData#getWarningSet()
|
* @see FlightData#getWarningSet()
|
||||||
*/
|
*/
|
||||||
public WarningSet getSimulatedWarnings() {
|
public WarningSet getSimulatedWarnings() {
|
||||||
|
mutex.verify();
|
||||||
if (simulatedData == null)
|
if (simulatedData == null)
|
||||||
return null;
|
return null;
|
||||||
return simulatedData.getWarningSet();
|
return simulatedData.getWarningSet();
|
||||||
@ -321,6 +349,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @see Rocket#getMotorConfigurationNameOrDescription(String)
|
* @see Rocket#getMotorConfigurationNameOrDescription(String)
|
||||||
*/
|
*/
|
||||||
public String getSimulatedMotorDescription() {
|
public String getSimulatedMotorDescription() {
|
||||||
|
mutex.verify();
|
||||||
return simulatedMotors;
|
return simulatedMotors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,6 +360,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
* @return the flight data of the previous simulation, or <code>null</code>.
|
* @return the flight data of the previous simulation, or <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public FlightData getSimulatedData() {
|
public FlightData getSimulatedData() {
|
||||||
|
mutex.verify();
|
||||||
return simulatedData;
|
return simulatedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +374,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Simulation copy() {
|
public Simulation copy() {
|
||||||
|
mutex.lock("copy");
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Simulation copy = (Simulation) super.clone();
|
Simulation copy = (Simulation) super.clone();
|
||||||
@ -359,9 +390,10 @@ public class Simulation implements ChangeSource, Cloneable {
|
|||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
|
|
||||||
|
|
||||||
} catch (CloneNotSupportedException e) {
|
} catch (CloneNotSupportedException e) {
|
||||||
throw new BugException("Clone not supported, BUG", 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")
|
@SuppressWarnings("unchecked")
|
||||||
public Simulation duplicateSimulation(Rocket newRocket) {
|
public Simulation duplicateSimulation(Rocket newRocket) {
|
||||||
Simulation copy = new Simulation(newRocket);
|
mutex.lock("duplicateSimulation");
|
||||||
|
try {
|
||||||
|
Simulation copy = new Simulation(newRocket);
|
||||||
|
|
||||||
copy.name = this.name;
|
copy.name = this.name;
|
||||||
copy.conditions.copyFrom(this.conditions);
|
copy.conditions.copyFrom(this.conditions);
|
||||||
copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
|
copy.simulationListeners = (ArrayList<String>) this.simulationListeners.clone();
|
||||||
copy.simulationStepperClass = this.simulationStepperClass;
|
copy.simulationStepperClass = this.simulationStepperClass;
|
||||||
copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
|
copy.aerodynamicCalculatorClass = this.aerodynamicCalculatorClass;
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
|
} finally {
|
||||||
|
mutex.unlock("duplicateSimulation");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addChangeListener(ChangeListener listener) {
|
public void addChangeListener(ChangeListener listener) {
|
||||||
|
mutex.verify();
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(ChangeListener listener) {
|
public void removeChangeListener(ChangeListener listener) {
|
||||||
|
mutex.verify();
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,8 @@ public class DebugLogDialog extends JDialog {
|
|||||||
panel.add(new JLabel("Display log lines:"), "gapright para, split");
|
panel.add(new JLabel("Display log lines:"), "gapright para, split");
|
||||||
for (LogLevel l : LogLevel.values()) {
|
for (LogLevel l : LogLevel.values()) {
|
||||||
JCheckBox box = new JCheckBox(l.toString());
|
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() {
|
box.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
@ -279,6 +280,7 @@ public class DebugLogDialog extends JDialog {
|
|||||||
sorter.setComparator(1, NumericComparator.INSTANCE);
|
sorter.setComparator(1, NumericComparator.INSTANCE);
|
||||||
sorter.setComparator(4, new LocationComparator());
|
sorter.setComparator(4, new LocationComparator());
|
||||||
table.setRowSorter(sorter);
|
table.setRowSorter(sorter);
|
||||||
|
sorter.setRowFilter(new LogFilter());
|
||||||
|
|
||||||
|
|
||||||
panel.add(new JScrollPane(table), "span, grow, width " +
|
panel.add(new JScrollPane(table), "span, grow, width " +
|
||||||
|
@ -3,14 +3,29 @@ package net.sf.openrocket.gui.dialogs;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTextArea;
|
||||||
|
|
||||||
|
import net.sf.openrocket.util.GUIUtil;
|
||||||
|
|
||||||
public class DetailDialog {
|
public class DetailDialog {
|
||||||
|
|
||||||
public static void showDetailedMessageDialog(Component parentComponent, Object message,
|
public static void showDetailedMessageDialog(Component parentComponent, Object message,
|
||||||
String details, String title, int messageType) {
|
String details, String title, int messageType) {
|
||||||
|
|
||||||
// TODO: HIGH: Detailed dialog
|
if (details != null) {
|
||||||
JOptionPane.showMessageDialog(parentComponent, message, title, messageType, 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 javax.swing.JDialog;
|
||||||
|
|
||||||
import net.sf.openrocket.document.OpenRocketDocument;
|
import net.sf.openrocket.document.OpenRocketDocument;
|
||||||
import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationParameter;
|
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
|
||||||
import net.sf.openrocket.optimization.rocketoptimization.RocketOptimizationParameterService;
|
import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameterService;
|
||||||
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
|
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
|
||||||
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifierService;
|
import net.sf.openrocket.optimization.rocketoptimization.SimulationModifierService;
|
||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
|
|
||||||
public class GeneralOptimizationDialog extends JDialog {
|
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 =
|
private final Map<Object, List<SimulationModifier>> simulationModifiers =
|
||||||
new HashMap<Object, List<SimulationModifier>>();
|
new HashMap<Object, List<SimulationModifier>>();
|
||||||
|
|
||||||
@ -36,10 +36,10 @@ public class GeneralOptimizationDialog extends JDialog {
|
|||||||
|
|
||||||
|
|
||||||
private void loadOptimizationParameters() {
|
private void loadOptimizationParameters() {
|
||||||
ServiceLoader<RocketOptimizationParameterService> loader =
|
ServiceLoader<OptimizableParameterService> loader =
|
||||||
ServiceLoader.load(RocketOptimizationParameterService.class);
|
ServiceLoader.load(OptimizableParameterService.class);
|
||||||
|
|
||||||
for (RocketOptimizationParameterService g : loader) {
|
for (OptimizableParameterService g : loader) {
|
||||||
optimizationParameters.addAll(g.getParameters(document));
|
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.");
|
throw new BugException("No rocket optimization parameters found, distribution built wrong.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(optimizationParameters, new Comparator<RocketOptimizationParameter>() {
|
Collections.sort(optimizationParameters, new Comparator<OptimizableParameter>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(RocketOptimizationParameter o1, RocketOptimizationParameter o2) {
|
public int compare(OptimizableParameter o1, OptimizableParameter o2) {
|
||||||
return o1.getName().compareTo(o2.getName());
|
return o1.getName().compareTo(o2.getName());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,8 @@ import java.net.URL;
|
|||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import javax.swing.Action;
|
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.BugReportDialog;
|
||||||
import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
|
import net.sf.openrocket.gui.dialogs.ComponentAnalysisDialog;
|
||||||
import net.sf.openrocket.gui.dialogs.DebugLogDialog;
|
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.ExampleDesignDialog;
|
||||||
import net.sf.openrocket.gui.dialogs.LicenseDialog;
|
import net.sf.openrocket.gui.dialogs.LicenseDialog;
|
||||||
import net.sf.openrocket.gui.dialogs.MotorDatabaseLoadingDialog;
|
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.BugException;
|
||||||
import net.sf.openrocket.util.GUIUtil;
|
import net.sf.openrocket.util.GUIUtil;
|
||||||
import net.sf.openrocket.util.Icons;
|
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.OpenFileWorker;
|
||||||
import net.sf.openrocket.util.Prefs;
|
import net.sf.openrocket.util.Prefs;
|
||||||
import net.sf.openrocket.util.Reflection;
|
import net.sf.openrocket.util.Reflection;
|
||||||
@ -728,10 +733,86 @@ public class BasicFrame extends JFrame {
|
|||||||
});
|
});
|
||||||
menu.add(item);
|
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();
|
menu.addSeparator();
|
||||||
|
|
||||||
item = new JMenuItem("Exception here");
|
item = new JMenuItem("Exception here");
|
||||||
item.addActionListener(new ActionListener() {
|
item.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
log.user("Exception here selected");
|
log.user("Exception here selected");
|
||||||
throw new RuntimeException("Testing exception from menu action listener");
|
throw new RuntimeException("Testing exception from menu action listener");
|
||||||
@ -769,13 +850,22 @@ public class BasicFrame extends JFrame {
|
|||||||
});
|
});
|
||||||
menu.add(item);
|
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;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select the tab on the main pane.
|
* 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.gui.dialogs.BugReportDialog;
|
||||||
import net.sf.openrocket.logging.LogHelper;
|
import net.sf.openrocket.logging.LogHelper;
|
||||||
|
import net.sf.openrocket.logging.TraceException;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.util.BugException;
|
|
||||||
|
|
||||||
|
|
||||||
public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||||
@ -63,6 +63,7 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||||||
} else {
|
} else {
|
||||||
log.info("Exception handler not on EDT, invoking dialog on EDT");
|
log.info("Exception handler not on EDT, invoking dialog on EDT");
|
||||||
SwingUtilities.invokeAndWait(new Runnable() {
|
SwingUtilities.invokeAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
showDialog(thread, throwable);
|
showDialog(thread, throwable);
|
||||||
}
|
}
|
||||||
@ -76,7 +77,7 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||||||
log.error("Caught exception while handling exception", ex);
|
log.error("Caught exception while handling exception", ex);
|
||||||
System.err.println("Exception in exception handler, dumping exception:");
|
System.err.println("Exception in exception handler, dumping exception:");
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
} catch (Throwable ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
@ -90,11 +91,14 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||||||
/**
|
/**
|
||||||
* Handle an error condition programmatically without throwing an exception.
|
* Handle an error condition programmatically without throwing an exception.
|
||||||
* This can be used in cases where recovery of the error is desirable.
|
* 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 message the error message.
|
||||||
*/
|
*/
|
||||||
public static void handleErrorCondition(String message) {
|
public static void handleErrorCondition(String message) {
|
||||||
log.error(1, message);
|
log.error(1, message, new TraceException());
|
||||||
handleErrorCondition(new InternalException(message));
|
handleErrorCondition(new InternalException(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +106,9 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||||||
/**
|
/**
|
||||||
* Handle an error condition programmatically without throwing an exception.
|
* Handle an error condition programmatically without throwing an exception.
|
||||||
* This can be used in cases where recovery of the error is desirable.
|
* 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 message the error message.
|
||||||
* @param exception the exception that occurred.
|
* @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.
|
* Handle an error condition programmatically without throwing an exception.
|
||||||
* This can be used in cases where recovery of the error is desirable.
|
* 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.
|
* @param exception the exception that occurred.
|
||||||
*/
|
*/
|
||||||
public static void handleErrorCondition(final Exception exception) {
|
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 {
|
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()) {
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
log.info("Running in EDT, showing dialog");
|
log.info("Running in EDT, showing dialog");
|
||||||
handler.showDialog(thread, exception);
|
handler.showDialog(thread, exception);
|
||||||
} else {
|
} else {
|
||||||
log.info("Not in EDT, invoking and waiting for dialog");
|
log.info("Not in EDT, invoking and waiting for dialog");
|
||||||
SwingUtilities.invokeAndWait(new Runnable() {
|
SwingUtilities.invokeAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
handler.showDialog(thread, exception);
|
handler.showDialog(thread, exception);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Exception occurred while showing error dialog", e);
|
log.error("Exception occurred in error handler", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +299,8 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||||||
*/
|
*/
|
||||||
private static boolean isNonFatalJREBug(Throwable t) {
|
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.
|
* 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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,6 @@ public class SimulationPanel extends JPanel {
|
|||||||
|
|
||||||
// Set simulation status icon
|
// Set simulation status icon
|
||||||
Simulation.Status status = document.getSimulation(row).getStatus();
|
Simulation.Status status = document.getSimulation(row).getStatus();
|
||||||
System.out.println("status=" + status);
|
|
||||||
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
|
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +69,13 @@ public class SimulationRunDialog extends JDialog {
|
|||||||
private final JProgressBar progressBar;
|
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 Simulation[] simulations;
|
||||||
|
private final String[] simulationNames;
|
||||||
private final SimulationWorker[] simulationWorkers;
|
private final SimulationWorker[] simulationWorkers;
|
||||||
private final SimulationStatus[] simulationStatuses;
|
private final SimulationStatus[] simulationStatuses;
|
||||||
private final double[] simulationMaxAltitude;
|
private final double[] simulationMaxAltitude;
|
||||||
@ -87,6 +93,7 @@ public class SimulationRunDialog extends JDialog {
|
|||||||
|
|
||||||
// Initialize the simulations
|
// Initialize the simulations
|
||||||
int n = simulations.length;
|
int n = simulations.length;
|
||||||
|
simulationNames = new String[n];
|
||||||
simulationWorkers = new SimulationWorker[n];
|
simulationWorkers = new SimulationWorker[n];
|
||||||
simulationStatuses = new SimulationStatus[n];
|
simulationStatuses = new SimulationStatus[n];
|
||||||
simulationMaxAltitude = new double[n];
|
simulationMaxAltitude = new double[n];
|
||||||
@ -94,6 +101,7 @@ public class SimulationRunDialog extends JDialog {
|
|||||||
simulationDone = new boolean[n];
|
simulationDone = new boolean[n];
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
|
simulationNames[i] = simulations[i].getName();
|
||||||
simulationWorkers[i] = new InteractiveSimulationWorker(simulations[i], i);
|
simulationWorkers[i] = new InteractiveSimulationWorker(simulations[i], i);
|
||||||
executor.execute(simulationWorkers[i]);
|
executor.execute(simulationWorkers[i]);
|
||||||
}
|
}
|
||||||
@ -201,7 +209,7 @@ public class SimulationRunDialog extends JDialog {
|
|||||||
log.debug("Progressbar value " + progress);
|
log.debug("Progressbar value " + progress);
|
||||||
|
|
||||||
// Update the simulation fields
|
// Update the simulation fields
|
||||||
simLabel.setText("Running " + simulations[index].getName());
|
simLabel.setText("Running " + simulationNames[index]);
|
||||||
if (simulationStatuses[index] == null) {
|
if (simulationStatuses[index] == null) {
|
||||||
log.debug("No simulation status data available, setting empty labels");
|
log.debug("No simulation status data available, setting empty labels");
|
||||||
timeLabel.setText("");
|
timeLabel.setText("");
|
||||||
|
@ -54,8 +54,6 @@ public abstract class SimulationWorker extends SwingWorker<FlightData, Simulatio
|
|||||||
try {
|
try {
|
||||||
simulation.simulate(listeners);
|
simulation.simulate(listeners);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
// System.out.println("Simulation interrupted:");
|
|
||||||
// e.printStackTrace();
|
|
||||||
throwable = e;
|
throwable = e;
|
||||||
return null;
|
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 after a simulation is successfully simulated. This method is not
|
||||||
* called if the simulation ends in an exception.
|
* called if the simulation ends in an exception.
|
||||||
*
|
|
||||||
* @param sim the simulation including the flight data
|
|
||||||
*/
|
*/
|
||||||
protected abstract void simulationDone();
|
protected abstract void simulationDone();
|
||||||
|
|
||||||
|
@ -608,7 +608,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
|||||||
@Override
|
@Override
|
||||||
protected void simulationDone() {
|
protected void simulationDone() {
|
||||||
// Do nothing if cancelled
|
// Do nothing if cancelled
|
||||||
if (isCancelled() || backgroundSimulationWorker != this) // Double-check
|
if (isCancelled() || backgroundSimulationWorker != this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
backgroundSimulationWorker = null;
|
backgroundSimulationWorker = null;
|
||||||
@ -658,6 +658,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
|||||||
* Updates the selection in the FigureParameters and repaints the figure.
|
* Updates the selection in the FigureParameters and repaints the figure.
|
||||||
* Ignores the event itself.
|
* Ignores the event itself.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void valueChanged(TreeSelectionEvent e) {
|
public void valueChanged(TreeSelectionEvent e) {
|
||||||
TreePath[] paths = selectionModel.getSelectionPaths();
|
TreePath[] paths = selectionModel.getSelectionPaths();
|
||||||
if (paths == null) {
|
if (paths == null) {
|
||||||
@ -688,6 +689,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
|||||||
figure.addChangeListener(this);
|
figure.addChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
boolean state = (Boolean) getValue(Action.SELECTED_KEY);
|
boolean state = (Boolean) getValue(Action.SELECTED_KEY);
|
||||||
if (state == true) {
|
if (state == true) {
|
||||||
@ -698,6 +700,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
|||||||
stateChanged(null);
|
stateChanged(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void stateChanged(ChangeEvent e) {
|
public void stateChanged(ChangeEvent e) {
|
||||||
putValue(Action.SELECTED_KEY, figure.getType() == type);
|
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>message</code> the String message (may be null).
|
||||||
* <li><code>cause</code> the exception that caused this log (may be null).
|
* <li><code>cause</code> the exception that caused this log (may be null).
|
||||||
* </ul>
|
* </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>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
@ -56,7 +59,11 @@ public abstract class LogHelper {
|
|||||||
* @param message the logged message (may be null).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void verbose(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void verbose(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void verbose(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void verbose(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void debug(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void debug(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void debug(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void debug(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void info(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void info(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void info(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void info(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void user(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void user(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void user(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void user(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void warn(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void warn(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void warn(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void warn(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void error(String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void error(String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void error(int levels, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void error(int levels, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void log(LogLevel level, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void log(LogLevel level, String message, Throwable cause) {
|
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).
|
* @param message the logged message (may be null).
|
||||||
*/
|
*/
|
||||||
public void log(int levels, LogLevel level, String message) {
|
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).
|
* @param cause the causing exception (may be null).
|
||||||
*/
|
*/
|
||||||
public void log(int levels, LogLevel level, String message, Throwable cause) {
|
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;
|
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 */
|
/** The maximum length of a level textual description */
|
||||||
public static final int LENGTH;
|
public static final int LENGTH;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
int length = 0;
|
int length = 0;
|
||||||
for (LogLevel l : LogLevel.values()) {
|
for (LogLevel l : LogLevel.values()) {
|
||||||
length = Math.max(length, l.toString().length());
|
length = Math.max(length, l.toString().length());
|
||||||
}
|
}
|
||||||
LENGTH = 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 (misses > 0) {
|
||||||
if (logs.isEmpty()) {
|
if (logs.isEmpty()) {
|
||||||
result.add(new LogLine(level, 0, 0, new TraceException(),
|
result.add(new LogLine(level, 0, 0, null,
|
||||||
"--- " + misses + " " + level + " lines removed but log is empty! ---",
|
"===== " + misses + " " + level + " lines removed but log is empty! =====",
|
||||||
null));
|
null));
|
||||||
} else {
|
} else {
|
||||||
result.add(new LogLine(level, logs.get(0).getLogCount(), 0, new TraceException(),
|
result.add(new LogLine(level, logs.get(0).getLogCount(), 0, null,
|
||||||
"--- " + misses + " " + level + " lines removed ---", null));
|
"===== " + misses + " " + level + " lines removed =====", null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.addAll(logs);
|
result.addAll(logs);
|
||||||
|
@ -32,11 +32,30 @@ public class LogLine implements Comparable<LogLine> {
|
|||||||
private volatile String formattedMessage = null;
|
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) {
|
public LogLine(LogLevel level, TraceException trace, String message, Throwable cause) {
|
||||||
this(level, logCount.getAndIncrement(), System.currentTimeMillis() - startTime, trace, message, cause);
|
this(level, logCount.getAndIncrement(), System.currentTimeMillis() - startTime, trace, message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
public LogLine(LogLevel level, int count, long timestamp,
|
||||||
TraceException trace, String message, Throwable cause) {
|
TraceException trace, String message, Throwable cause) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
@ -109,7 +128,7 @@ public class LogLine implements Comparable<LogLine> {
|
|||||||
if (formattedMessage == null) {
|
if (formattedMessage == null) {
|
||||||
String str;
|
String str;
|
||||||
str = String.format("%4d %10.3f %-" + LogLevel.LENGTH + "s %s %s",
|
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() : "(-)",
|
(trace != null) ? trace.getMessage() : "(-)",
|
||||||
message);
|
message);
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
|
@ -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.
|
* 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.
|
* @param point the point at which to evaluate the function.
|
||||||
* @return the function value.
|
* @return the function value.
|
||||||
* @throws InterruptedException if the thread was interrupted before function evaluation was completed.
|
* @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;
|
public double evaluate(Point point) throws InterruptedException, OptimizationException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
* 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>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
public interface FunctionCache {
|
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);
|
public double getValue(Point point);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache.
|
||||||
|
*/
|
||||||
public void clearCache();
|
public void clearCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the function that is evaluated by this cache implementation.
|
||||||
|
*
|
||||||
|
* @return the function that is being evaluated.
|
||||||
|
*/
|
||||||
public Function getFunction();
|
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);
|
public void setFunction(Function function);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,9 @@ public interface FunctionOptimizer {
|
|||||||
*
|
*
|
||||||
* @param initial the initial start point of the optimization.
|
* @param initial the initial start point of the optimization.
|
||||||
* @param control the optimization control.
|
* @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.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import net.sf.openrocket.util.BugException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of a ParallelFunctionCache that evaluates function values
|
* An implementation of a ParallelFunctionCache that evaluates function values
|
||||||
* in parallel and caches them. This allows pre-calculating possibly required
|
* in parallel and caches them. This allows pre-calculating possibly required
|
||||||
* function values beforehand. If values are not required after all, the
|
* function values beforehand. If values are not required after all, the
|
||||||
* computation can be aborted assuming the function evaluation supports it.
|
* 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>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
@ -32,13 +38,22 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
private Function function;
|
private Function function;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a cache that uses the same number of computational threads as there are
|
||||||
|
* processors available.
|
||||||
|
*/
|
||||||
public ParallelExecutorCache() {
|
public ParallelExecutorCache() {
|
||||||
this(Runtime.getRuntime().availableProcessors());
|
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) {
|
public ParallelExecutorCache(int threadCount) {
|
||||||
executor = new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
|
this(new ThreadPoolExecutor(threadCount, threadCount, 60, TimeUnit.SECONDS,
|
||||||
new LinkedBlockingQueue<Runnable>(),
|
new LinkedBlockingQueue<Runnable>(),
|
||||||
new ThreadFactory() {
|
new ThreadFactory() {
|
||||||
@Override
|
@Override
|
||||||
@ -47,20 +62,22 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
return t;
|
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) {
|
public ParallelExecutorCache(ExecutorService executor) {
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Queue a list of function evaluations at the specified points.
|
|
||||||
*
|
|
||||||
* @param points the points at which to evaluate the function.
|
|
||||||
*/
|
|
||||||
public void compute(Collection<Point> points) {
|
public void compute(Collection<Point> points) {
|
||||||
for (Point p : points) {
|
for (Point p : points) {
|
||||||
compute(p);
|
compute(p);
|
||||||
@ -68,11 +85,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Queue function evaluation for the specified point.
|
|
||||||
*
|
|
||||||
* @param point the point at which to evaluate the function.
|
|
||||||
*/
|
|
||||||
public void compute(Point point) {
|
public void compute(Point point) {
|
||||||
if (functionCache.containsKey(point)) {
|
if (functionCache.containsKey(point)) {
|
||||||
// Function has already been evaluated at the point
|
// Function has already been evaluated at the point
|
||||||
@ -84,13 +97,6 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
return;
|
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
|
// Submit point for evaluation
|
||||||
FunctionCallable callable = new FunctionCallable(function, point);
|
FunctionCallable callable = new FunctionCallable(function, point);
|
||||||
Future<Double> future = executor.submit(callable);
|
Future<Double> future = executor.submit(callable);
|
||||||
@ -98,27 +104,16 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Wait for a collection of points to be computed. After calling this method
|
public void waitFor(Collection<Point> points) throws InterruptedException, OptimizationException {
|
||||||
* 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 {
|
|
||||||
for (Point p : points) {
|
for (Point p : points) {
|
||||||
waitFor(p);
|
waitFor(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for a point to be computed. After calling this method
|
@Override
|
||||||
* the function values are available by calling XXX
|
public void waitFor(Point point) throws InterruptedException, OptimizationException {
|
||||||
*
|
|
||||||
* @param point the point to wait for.
|
|
||||||
* @throws InterruptedException if this thread was interrupted while waiting.
|
|
||||||
*/
|
|
||||||
public void waitFor(Point point) throws InterruptedException {
|
|
||||||
if (functionCache.containsKey(point)) {
|
if (functionCache.containsKey(point)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -132,18 +127,24 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
double value = future.get();
|
double value = future.get();
|
||||||
functionCache.put(point, value);
|
functionCache.put(point, value);
|
||||||
} catch (ExecutionException e) {
|
} 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,
|
@Override
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
public List<Point> abort(Collection<Point> points) {
|
public List<Point> abort(Collection<Point> points) {
|
||||||
List<Point> computed = new ArrayList<Point>(Math.min(points.size(), 10));
|
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,
|
@Override
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public boolean abort(Point point) {
|
public boolean abort(Point point) {
|
||||||
if (functionCache.containsKey(point)) {
|
if (functionCache.containsKey(point)) {
|
||||||
return true;
|
return true;
|
||||||
@ -191,17 +187,17 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public double getValue(Point point) {
|
public double getValue(Point point) {
|
||||||
Double d = functionCache.get(point);
|
Double d = functionCache.get(point);
|
||||||
if (d == null) {
|
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);
|
"functionCache=" + functionCache + " futureMap=" + futureMap);
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Function getFunction() {
|
public Function getFunction() {
|
||||||
return function;
|
return function;
|
||||||
@ -220,6 +216,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
functionCache.clear();
|
functionCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ExecutorService getExecutor() {
|
public ExecutorService getExecutor() {
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
@ -239,7 +236,7 @@ public class ParallelExecutorCache implements ParallelFunctionCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double call() throws InterruptedException {
|
public Double call() throws InterruptedException, OptimizationException {
|
||||||
return calledFunction.evaluate(point);
|
return calledFunction.evaluate(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
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
|
* waiting for specific points to become computed or aborting the computation of
|
||||||
* points.
|
* points.
|
||||||
*
|
*
|
||||||
@ -13,14 +13,17 @@ import java.util.List;
|
|||||||
public interface ParallelFunctionCache extends FunctionCache {
|
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.
|
* @param points the points at which to evaluate the function.
|
||||||
*/
|
*/
|
||||||
public void compute(Collection<Point> points);
|
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.
|
* @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
|
* 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.
|
* @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
|
* 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.
|
* @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.
|
* the result is stored in the function cache anyway.
|
||||||
*
|
*
|
||||||
* @param points the points to abort.
|
* @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.FunctionCache;
|
||||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
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.ParallelFunctionCache;
|
||||||
import net.sf.openrocket.optimization.general.Point;
|
import net.sf.openrocket.optimization.general.Point;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
@ -48,7 +49,7 @@ public class MultidirectionalSearchOptimizer implements FunctionOptimizer, Stati
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void optimize(Point initial, OptimizationController control) {
|
public void optimize(Point initial, OptimizationController control) throws OptimizationException {
|
||||||
FunctionCacheComparator comparator = new FunctionCacheComparator(functionExecutor);
|
FunctionCacheComparator comparator = new FunctionCacheComparator(functionExecutor);
|
||||||
|
|
||||||
final List<Point> pattern = SearchPattern.square(initial.dim());
|
final List<Point> pattern = SearchPattern.square(initial.dim());
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.sf.openrocket.optimization.rocketoptimization;
|
package net.sf.openrocket.optimization.rocketoptimization;
|
||||||
|
|
||||||
import net.sf.openrocket.document.Simulation;
|
import net.sf.openrocket.document.Simulation;
|
||||||
|
import net.sf.openrocket.optimization.general.OptimizationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A parameter of a rocket or simulation that can be optimized
|
* 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>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
public interface RocketOptimizationParameter {
|
public interface OptimizableParameter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the label name for this optimization parameter.
|
* 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.
|
* Compute the value for this optimization parameter for the simulation.
|
||||||
* The return value can be any double value.
|
* The return value can be any double value.
|
||||||
|
* <p>
|
||||||
|
* This method can return NaN in case of a problem computing
|
||||||
*
|
*
|
||||||
* @param simulation the simulation
|
* @param simulation the simulation
|
||||||
* @return the parameter value (any double value)
|
* @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>
|
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||||
*/
|
*/
|
||||||
public interface RocketOptimizationParameterService {
|
public interface OptimizableParameterService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all available rocket optimization parameters for this document.
|
* Return all available rocket optimization parameters for this document.
|
||||||
@ -18,6 +18,6 @@ public interface RocketOptimizationParameterService {
|
|||||||
* @param document the design document
|
* @param document the design document
|
||||||
* @return a collection of the rocket optimization parameters.
|
* @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.document.Simulation;
|
||||||
import net.sf.openrocket.logging.LogHelper;
|
import net.sf.openrocket.logging.LogHelper;
|
||||||
import net.sf.openrocket.optimization.general.Function;
|
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.optimization.general.Point;
|
||||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
@ -20,13 +21,16 @@ import net.sf.openrocket.startup.Application;
|
|||||||
public class RocketOptimizationFunction implements Function {
|
public class RocketOptimizationFunction implements Function {
|
||||||
private static final LogHelper log = Application.getLogger();
|
private static final LogHelper log = Application.getLogger();
|
||||||
|
|
||||||
|
private static final double OUTSIDE_DOMAIN_SCALE = 1.0e200;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: This class must be thread-safe!!!
|
* NOTE: This class must be thread-safe!!!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private final Simulation baseSimulation;
|
private final Simulation baseSimulation;
|
||||||
private final RocketOptimizationParameter parameter;
|
private final OptimizableParameter parameter;
|
||||||
private final OptimizationGoal goal;
|
private final OptimizationGoal goal;
|
||||||
|
private final SimulationDomain domain;
|
||||||
private final SimulationModifier[] modifiers;
|
private final SimulationModifier[] modifiers;
|
||||||
|
|
||||||
private final Map<Point, Double> parameterValueCache = new ConcurrentHashMap<Point, Double>();
|
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 goal the goal of the rocket parameter
|
||||||
* @param modifiers the modifiers that modify the simulation
|
* @param modifiers the modifiers that modify the simulation
|
||||||
*/
|
*/
|
||||||
public RocketOptimizationFunction(Simulation baseSimulation, RocketOptimizationParameter parameter,
|
public RocketOptimizationFunction(Simulation baseSimulation, OptimizableParameter parameter,
|
||||||
OptimizationGoal goal, SimulationModifier... modifiers) {
|
OptimizationGoal goal, SimulationDomain domain, SimulationModifier... modifiers) {
|
||||||
this.baseSimulation = baseSimulation;
|
this.baseSimulation = baseSimulation;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
this.goal = goal;
|
this.goal = goal;
|
||||||
|
this.domain = domain;
|
||||||
this.modifiers = modifiers.clone();
|
this.modifiers = modifiers.clone();
|
||||||
if (modifiers.length == 0) {
|
if (modifiers.length == 0) {
|
||||||
throw new IllegalArgumentException("No SimulationModifiers specified");
|
throw new IllegalArgumentException("No SimulationModifiers specified");
|
||||||
@ -57,50 +62,69 @@ public class RocketOptimizationFunction implements Function {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@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
|
// Check for precomputed value
|
||||||
double value = preComputed(point);
|
Double d = goalValueCache.get(point);
|
||||||
if (!Double.isNaN(value)) {
|
if (d != null && !Double.isNaN(d)) {
|
||||||
return value;
|
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
|
// Create the new simulation based on the point
|
||||||
double[] p = point.asArray();
|
double[] p = point.asArray();
|
||||||
if (p.length != modifiers.length) {
|
if (p.length != modifiers.length) {
|
||||||
throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
|
throw new IllegalArgumentException("Point has length " + p.length + " while function has " +
|
||||||
modifiers.length + " simulation modifiers");
|
modifiers.length + " simulation modifiers");
|
||||||
}
|
}
|
||||||
Simulation simulation = newSimulationInstance();
|
|
||||||
|
Simulation simulation = newSimulationInstance(baseSimulation);
|
||||||
for (int i = 0; i < modifiers.length; i++) {
|
for (int i = 0; i < modifiers.length; i++) {
|
||||||
modifiers[i].modify(simulation, p[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
|
// Compute the optimization value
|
||||||
value = parameter.computeValue(simulation);
|
parameterValue = parameter.computeValue(simulation);
|
||||||
parameterValueCache.put(point, value);
|
parameterValueCache.put(point, parameterValue);
|
||||||
|
|
||||||
value = goal.getMinimizationParameter(value);
|
goalValue = goal.getMinimizationParameter(parameterValue);
|
||||||
if (Double.isNaN(value)) {
|
if (Double.isNaN(goalValue)) {
|
||||||
log.warn("Computed value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
|
log.warn("Computed goal value was NaN, baseSimulation=" + baseSimulation + " parameter=" + parameter +
|
||||||
" goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation);
|
" goal=" + goal + " modifiers=" + Arrays.toString(modifiers) + " simulation=" + simulation +
|
||||||
value = Double.MAX_VALUE;
|
" parameter value=" + parameterValue);
|
||||||
|
goalValue = Double.MAX_VALUE;
|
||||||
}
|
}
|
||||||
goalValueCache.put(point, value);
|
goalValueCache.put(point, goalValue);
|
||||||
|
|
||||||
return value;
|
log.verbose("Parameter value at point " + point + " is " + goalValue + ", goal function value=" + goalValue);
|
||||||
|
|
||||||
|
return goalValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public double preComputed(Point point) {
|
|
||||||
Double value = goalValueCache.get(point);
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: : is in domain?
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,13 +147,15 @@ public class RocketOptimizationFunction implements Function {
|
|||||||
/**
|
/**
|
||||||
* Returns a new deep copy of the simulation and rocket. This methods performs
|
* Returns a new deep copy of the simulation and rocket. This methods performs
|
||||||
* synchronization on the simulation for thread protection.
|
* 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) {
|
synchronized (baseSimulation) {
|
||||||
Rocket newRocket = (Rocket) baseSimulation.getRocket().copy();
|
Rocket newRocket = (Rocket) simulation.getRocket().copy();
|
||||||
Simulation newSimulation = baseSimulation.duplicateSimulation(newRocket);
|
Simulation newSimulation = simulation.duplicateSimulation(newRocket);
|
||||||
return newSimulation;
|
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
|
* 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.
|
* 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();
|
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.ChangeListener;
|
||||||
import javax.swing.event.EventListenerList;
|
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.motor.Motor;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.util.Chars;
|
import net.sf.openrocket.util.Chars;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
import net.sf.openrocket.util.MathUtil;
|
import net.sf.openrocket.util.MathUtil;
|
||||||
@ -30,9 +33,9 @@ import net.sf.openrocket.util.UniqueID;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class Rocket extends RocketComponent {
|
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) {
|
public void addComponentChangeListener(ComponentChangeListener l) {
|
||||||
checkState();
|
checkState();
|
||||||
listenerList.add(ComponentChangeListener.class, l);
|
listenerList.add(ComponentChangeListener.class, l);
|
||||||
if (DEBUG_LISTENERS)
|
log.verbose("Added ComponentChangeListener " + l + ", current number of listeners is " +
|
||||||
System.out.println(this + ": Added listner (now " + listenerList.getListenerCount() +
|
listenerList.getListenerCount());
|
||||||
" listeners): " + l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeComponentChangeListener(ComponentChangeListener l) {
|
public void removeComponentChangeListener(ComponentChangeListener l) {
|
||||||
listenerList.remove(ComponentChangeListener.class, l);
|
listenerList.remove(ComponentChangeListener.class, l);
|
||||||
if (DEBUG_LISTENERS)
|
log.verbose("Removed ComponentChangeListener " + l + ", current number of listeners is " +
|
||||||
System.out.println(this + ": Removed listner (now " + listenerList.getListenerCount() +
|
listenerList.getListenerCount());
|
||||||
" listeners): " + l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -361,17 +362,15 @@ public class Rocket extends RocketComponent {
|
|||||||
public void addChangeListener(ChangeListener l) {
|
public void addChangeListener(ChangeListener l) {
|
||||||
checkState();
|
checkState();
|
||||||
listenerList.add(ChangeListener.class, l);
|
listenerList.add(ChangeListener.class, l);
|
||||||
if (DEBUG_LISTENERS)
|
log.verbose("Added ChangeListener " + l + ", current number of listeners is " +
|
||||||
System.out.println(this + ": Added listner (now " + listenerList.getListenerCount() +
|
listenerList.getListenerCount());
|
||||||
" listeners): " + l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeChangeListener(ChangeListener l) {
|
public void removeChangeListener(ChangeListener l) {
|
||||||
listenerList.remove(ChangeListener.class, l);
|
listenerList.remove(ChangeListener.class, l);
|
||||||
if (DEBUG_LISTENERS)
|
log.verbose("Removed ChangeListener " + l + ", current number of listeners is " +
|
||||||
System.out.println(this + ": Removed listner (now " + listenerList.getListenerCount() +
|
listenerList.getListenerCount());
|
||||||
" listeners): " + l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -392,15 +391,15 @@ public class Rocket extends RocketComponent {
|
|||||||
functionalModID = modID;
|
functionalModID = modID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_LISTENERS)
|
|
||||||
System.out.println("FIRING " + e);
|
|
||||||
|
|
||||||
// Check whether frozen
|
// Check whether frozen
|
||||||
if (freezeList != null) {
|
if (freezeList != null) {
|
||||||
|
log.debug("Rocket is in frozen state, adding event " + e + " info freeze list");
|
||||||
freezeList.add(e);
|
freezeList.add(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("Firing rocket change event " + e);
|
||||||
|
|
||||||
// Notify all components first
|
// Notify all components first
|
||||||
Iterator<RocketComponent> iterator = this.deepIterator(true);
|
Iterator<RocketComponent> iterator = this.deepIterator(true);
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
@ -441,6 +440,10 @@ public class Rocket extends RocketComponent {
|
|||||||
checkState();
|
checkState();
|
||||||
if (freezeList == null) {
|
if (freezeList == null) {
|
||||||
freezeList = new LinkedList<ComponentChangeEvent>();
|
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() {
|
public void thaw() {
|
||||||
checkState();
|
checkState();
|
||||||
if (freezeList == null)
|
if (freezeList == null) {
|
||||||
|
ExceptionHandler.handleErrorCondition("Attempting to thaw Rocket when it is not frozen");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (freezeList.size() == 0) {
|
if (freezeList.size() == 0) {
|
||||||
|
log.warn("Thawing rocket with no changes made");
|
||||||
freezeList = null;
|
freezeList = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("Thawing rocket, freezeList=" + freezeList);
|
||||||
|
|
||||||
int type = 0;
|
int type = 0;
|
||||||
Object c = null;
|
Object c = null;
|
||||||
for (ComponentChangeEvent e : freezeList) {
|
for (ComponentChangeEvent e : freezeList) {
|
||||||
|
@ -12,7 +12,9 @@ import java.util.Stack;
|
|||||||
|
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import net.sf.openrocket.logging.LogHelper;
|
||||||
import net.sf.openrocket.logging.TraceException;
|
import net.sf.openrocket.logging.TraceException;
|
||||||
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.util.BugException;
|
import net.sf.openrocket.util.BugException;
|
||||||
import net.sf.openrocket.util.ChangeSource;
|
import net.sf.openrocket.util.ChangeSource;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
@ -23,6 +25,7 @@ import net.sf.openrocket.util.UniqueID;
|
|||||||
|
|
||||||
public abstract class RocketComponent implements ChangeSource, Cloneable,
|
public abstract class RocketComponent implements ChangeSource, Cloneable,
|
||||||
Iterable<RocketComponent> {
|
Iterable<RocketComponent> {
|
||||||
|
private static final LogHelper log = Application.getLogger();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Text is suitable to the form
|
* Text is suitable to the form
|
||||||
@ -1304,6 +1307,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable,
|
|||||||
checkState();
|
checkState();
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
/* Ignore if root invalid. */
|
/* Ignore if root invalid. */
|
||||||
|
log.debug("Attempted firing event " + e + " with root " + this.getComponentName() + ", ignoring event");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getRoot().fireComponentChangeEvent(e);
|
getRoot().fireComponentChangeEvent(e);
|
||||||
|
@ -41,6 +41,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
private SimulationStatus status;
|
private SimulationStatus status;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public FlightData simulate(SimulationConditions simulationConditions) throws SimulationException {
|
public FlightData simulate(SimulationConditions simulationConditions) throws SimulationException {
|
||||||
Set<MotorId> motorBurntOut = new HashSet<MotorId>();
|
Set<MotorId> motorBurntOut = new HashSet<MotorId>();
|
||||||
|
|
||||||
@ -409,7 +410,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
|
|
||||||
case STAGE_SEPARATION: {
|
case STAGE_SEPARATION: {
|
||||||
// TODO: HIGH: Store lower stages to be simulated later
|
// TODO: HIGH: Store lower stages to be simulated later
|
||||||
RocketComponent stage = (RocketComponent) event.getSource();
|
RocketComponent stage = event.getSource();
|
||||||
int n = stage.getStageNumber();
|
int n = stage.getStageNumber();
|
||||||
status.getConfiguration().setToStage(n);
|
status.getConfiguration().setToStage(n);
|
||||||
status.getFlightData().addEvent(event);
|
status.getFlightData().addEvent(event);
|
||||||
@ -542,6 +543,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
d += status.getEffectiveLaunchRodLength();
|
d += status.getEffectiveLaunchRodLength();
|
||||||
|
|
||||||
if (Double.isNaN(d) || b) {
|
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.");
|
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 {
|
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
|
// Initialize logging first so we can use it
|
||||||
initializeLogging();
|
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) {
|
private static void runMain(String[] args) {
|
||||||
|
|
||||||
// Initialize the splash screen with version info
|
// 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) {
|
public static void installEscapeCloseOperation(final JDialog dialog) {
|
||||||
Action dispatchClosing = new AbstractAction() {
|
Action dispatchClosing = new AbstractAction() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent event) {
|
public void actionPerformed(ActionEvent event) {
|
||||||
log.user("Closing dialog " + dialog);
|
log.user("Closing dialog " + dialog);
|
||||||
dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING));
|
dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING));
|
||||||
@ -194,6 +195,7 @@ public class GUIUtil {
|
|||||||
@Override
|
@Override
|
||||||
public void windowClosed(WindowEvent e) {
|
public void windowClosed(WindowEvent e) {
|
||||||
setNullModels(window);
|
setNullModels(window);
|
||||||
|
MemoryManagement.collectable(window);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -341,7 +343,7 @@ public class GUIUtil {
|
|||||||
|
|
||||||
JTree tree = (JTree) c;
|
JTree tree = (JTree) c;
|
||||||
tree.setModel(new DefaultTreeModel(new TreeNode() {
|
tree.setModel(new DefaultTreeModel(new TreeNode() {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("rawtypes")
|
||||||
@Override
|
@Override
|
||||||
public Enumeration children() {
|
public Enumeration children() {
|
||||||
return new Vector().elements();
|
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.Function;
|
||||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
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.ParallelExecutorCache;
|
||||||
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
import net.sf.openrocket.optimization.general.ParallelFunctionCache;
|
||||||
import net.sf.openrocket.optimization.general.Point;
|
import net.sf.openrocket.optimization.general.Point;
|
||||||
@ -22,7 +23,7 @@ public class TestFunctionOptimizer {
|
|||||||
|
|
||||||
|
|
||||||
private void go(final ParallelFunctionCache functionCache,
|
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() {
|
Function function = new Function() {
|
||||||
@Override
|
@Override
|
||||||
@ -35,15 +36,6 @@ public class TestFunctionOptimizer {
|
|||||||
return Double.NaN;
|
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() {
|
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());
|
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.Function;
|
||||||
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
import net.sf.openrocket.optimization.general.FunctionOptimizer;
|
||||||
import net.sf.openrocket.optimization.general.OptimizationController;
|
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.ParallelExecutorCache;
|
||||||
import net.sf.openrocket.optimization.general.Point;
|
import net.sf.openrocket.optimization.general.Point;
|
||||||
import net.sf.openrocket.optimization.general.multidim.MultidirectionalSearchOptimizer;
|
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() {
|
Function function = new Function() {
|
||||||
@Override
|
@Override
|
||||||
@ -34,15 +35,6 @@ public class TestFunctionOptimizerLoop {
|
|||||||
evaluations++;
|
evaluations++;
|
||||||
return p.sub(optimum).length2();
|
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() {
|
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);
|
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