From b7dc313ee8892ffe960a4f79770bcf2dae6c475e Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Sat, 19 Jun 2021 18:02:45 +0200 Subject: [PATCH 01/12] Fix auto-run simulation NA values --- .../BasicEventSimulationEngine.java | 5 ++-- .../sf/openrocket/simulation/FlightData.java | 2 +- .../listeners/system/GroundHitListener.java | 29 +++++++++++++++++++ .../gui/scalefigure/RocketPanel.java | 4 +-- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 core/src/net/sf/openrocket/simulation/listeners/system/GroundHitListener.java diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 82cccd9ed..a7db4f200 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -259,7 +259,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { private boolean handleEvents() throws SimulationException { boolean ret = true; FlightEvent event; - + log.trace("HandleEvents: current branch = " + currentStatus.getFlightData().getBranchName()); for (event = nextEvent(); event != null; event = nextEvent()) { log.trace("Obtained event from queue: " + event.toString()); @@ -300,7 +300,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Ignore events for components that are no longer attached to the rocket if (event.getSource() != null && event.getSource().getParent() != null && !currentStatus.getConfiguration().isComponentActive(event.getSource())) { - log.trace("Ignoring event from unattached componenent"); + log.trace("Ignoring event from unattached component"); continue; } @@ -332,6 +332,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for recovery device deployment, add events to queue + // TODO: LOW: check if deprecated function getActiveComponents needs to be replaced for (RocketComponent c : currentStatus.getConfiguration().getActiveComponents()) { if (!(c instanceof RecoveryDevice)) continue; diff --git a/core/src/net/sf/openrocket/simulation/FlightData.java b/core/src/net/sf/openrocket/simulation/FlightData.java index e7f24f806..6cdb356e1 100644 --- a/core/src/net/sf/openrocket/simulation/FlightData.java +++ b/core/src/net/sf/openrocket/simulation/FlightData.java @@ -220,7 +220,7 @@ public class FlightData { timeToApogee = Double.NaN; - // Launch rod velocity + // Launch rod velocity + deployment velocity + ground hit velocity for (FlightEvent event : branch.getEvents()) { if (event.getType() == FlightEvent.Type.LAUNCHROD) { double t = event.getTime(); diff --git a/core/src/net/sf/openrocket/simulation/listeners/system/GroundHitListener.java b/core/src/net/sf/openrocket/simulation/listeners/system/GroundHitListener.java new file mode 100644 index 000000000..96d9ffd1b --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/listeners/system/GroundHitListener.java @@ -0,0 +1,29 @@ +package net.sf.openrocket.simulation.listeners.system; + +import net.sf.openrocket.simulation.FlightEvent; +import net.sf.openrocket.simulation.SimulationStatus; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; + + +/** + * A simulation listeners that ends the simulation when the ground is hit. + * + * @author Sibo Van Gool + */ +public class GroundHitListener extends AbstractSimulationListener { + + public static final GroundHitListener INSTANCE = new GroundHitListener(); + + @Override + public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) { + if (event.getType() == FlightEvent.Type.GROUND_HIT) { + status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime())); + } + return true; + } + + @Override + public boolean isSystemListener() { + return true; + } +} diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 74b855abf..39445e35c 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -66,7 +66,7 @@ import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener; import net.sf.openrocket.simulation.listeners.SimulationListener; -import net.sf.openrocket.simulation.listeners.system.ApogeeEndListener; +import net.sf.openrocket.simulation.listeners.system.GroundHitListener; import net.sf.openrocket.simulation.listeners.system.InterruptListener; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; @@ -770,7 +770,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change protected SimulationListener[] getExtraListeners() { return new SimulationListener[] { InterruptListener.INSTANCE, - ApogeeEndListener.INSTANCE, + GroundHitListener.INSTANCE, exprListener }; } From 2c2a7a2d3b3c279aac6d85a640aa492d32bcf230 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Sun, 20 Jun 2021 22:06:58 +0200 Subject: [PATCH 02/12] Fix run sim after config change + fix sim table not updating --- .../net/sf/openrocket/document/OpenRocketDocument.java | 2 +- .../openrocket/simulation/BasicEventSimulationEngine.java | 3 ++- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 2 +- .../net/sf/openrocket/gui/scalefigure/RocketPanel.java | 8 +++++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/net/sf/openrocket/document/OpenRocketDocument.java b/core/src/net/sf/openrocket/document/OpenRocketDocument.java index eb0f26d7a..41c32acac 100644 --- a/core/src/net/sf/openrocket/document/OpenRocketDocument.java +++ b/core/src/net/sf/openrocket/document/OpenRocketDocument.java @@ -810,7 +810,7 @@ public class OpenRocketDocument implements ComponentChangeListener { listeners.remove(listener); } - protected void fireDocumentChangeEvent(DocumentChangeEvent event) { + public void fireDocumentChangeEvent(DocumentChangeEvent event) { DocumentChangeListener[] array = listeners.toArray(new DocumentChangeListener[0]); for (DocumentChangeListener l : array) { l.documentChanged(event); diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index a7db4f200..82723a6e1 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -533,7 +533,8 @@ public class BasicEventSimulationEngine implements SimulationEngine { } } - + + // TODO : FUTURE : do not hard code the 1200 (maybe even make it configurable by the user) if( 1200 < currentStatus.getSimulationTime() ){ ret = false; log.error("Simulation hit max time (1200s): aborting."); diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 2488234e7..7657bba57 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -576,7 +576,7 @@ public class SimulationPanel extends JPanel { public void documentChanged(DocumentChangeEvent event) { if (!(event instanceof SimulationChangeEvent)) return; - simulationTableModel.fireTableDataChanged(); + fireMaintainSelection(); } }); diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 39445e35c..1caf8388e 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -38,6 +38,7 @@ import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.document.events.SimulationChangeEvent; import net.sf.openrocket.gui.adaptors.DoubleModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.ConfigurationComboBox; @@ -216,6 +217,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change rkt.addComponentChangeListener(new ComponentChangeListener() { @Override public void componentChanged(ComponentChangeEvent e) { + updateExtras(); if (is3d) { if (e.isTextureChange()) { figure3d.flushTextureCaches(); @@ -557,7 +559,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /** * Updates the extra data included in the figure. Currently this includes - * the CP and CG carets. + * the CP and CG carets. Also start the background simulator. */ private WarningSet warnings = new WarningSet(); @@ -708,7 +710,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change simulation.setFlightConfigurationId( document.getSelectedConfiguration().getId()); } else System.out.println("using pre-existing simulation"); - + backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation); backgroundSimulationExecutor.execute(backgroundSimulationWorker); } @@ -758,12 +760,12 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // Do nothing if cancelled if (isCancelled() || backgroundSimulationWorker != this) return; - backgroundSimulationWorker = null; extraText.setFlightData(simulation.getSimulatedData()); extraText.setCalculatingData(false); figure.repaint(); figure3d.repaint(); + document.fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); } @Override From d74df08e04eee48d006ae3297119c55605932a0b Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Sun, 11 Jul 2021 23:14:42 +0200 Subject: [PATCH 03/12] [fixes #927] Fixed only current configuration updating instead of all the sims --- .../gui/scalefigure/RocketPanel.java | 124 ++++++++++++------ 1 file changed, 83 insertions(+), 41 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 1caf8388e..9b1e8f564 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -8,11 +8,11 @@ import java.awt.Point; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.util.ArrayList; -import java.util.Collection; import java.util.EventListener; import java.util.EventObject; import java.util.List; -import java.util.concurrent.Executor; +import java.util.LinkedList; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -63,6 +63,7 @@ import net.sf.openrocket.rocketcomponent.FlightConfigurationId; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.simulation.BasicEventSimulationEngine; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener; @@ -76,11 +77,13 @@ import net.sf.openrocket.util.Chars; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.StateChangeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * A JPanel that contains a RocketFigure and buttons to manipulate the figure. - * + * A JPanel that contains a RocketFigure and buttons to manipulate the figure. + * * @author Sampo Niskanen * @author Bill Kuker */ @@ -88,6 +91,7 @@ import net.sf.openrocket.util.StateChangeListener; public class RocketPanel extends JPanel implements TreeSelectionListener, ChangeSource { private static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(RocketPanel.class); public enum VIEW_TYPE { SideView(false, RocketFigure.VIEW_SIDE), @@ -129,7 +133,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /* Calculation of CP and CG */ private AerodynamicCalculator aerodynamicCalculator; - + private final OpenRocketDocument document; private Caret extraCP = null; @@ -155,7 +159,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change * This uses a fixed-sized thread pool for all background simulations * with all threads in daemon mode and with minimum priority. */ - private static final Executor backgroundSimulationExecutor; + private static final ExecutorService backgroundSimulationExecutor; static { backgroundSimulationExecutor = Executors.newFixedThreadPool(SwingPreferences.getMaxThreadCount(), new ThreadFactory() { @@ -171,7 +175,6 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change }); } - public OpenRocketDocument getDocument(){ return this.document; } @@ -369,7 +372,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /** * Get the center of pressure figure element. - * + * * @return center of pressure info */ public Caret getExtraCP() { @@ -378,7 +381,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /** * Get the center of gravity figure element. - * + * * @return center of gravity info */ public Caret getExtraCG() { @@ -387,7 +390,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /** * Get the extra text figure element. - * + * * @return extra text that contains info about the rocket design */ public RocketInfo getExtraText() { @@ -492,12 +495,12 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change /** * Handle clicking on figure shapes. The functioning is the following: - * + * * Get the components clicked. * If no component is clicked, do nothing. - * If the currently selected component is in the set, keep it, - * unless the selector specified is pressed. If it is pressed, cycle to - * the next component. Otherwise select the first component in the list. + * If the currently selected component is in the set, keep it, + * unless the selector specified is pressed. If it is pressed, cycle to + * the next component. Otherwise select the first component in the list. */ public static final int CYCLE_SELECTION_MODIFIER = InputEvent.SHIFT_DOWN_MASK; @@ -647,7 +650,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figure3d.setCG(new Coordinate(Double.NaN, Double.NaN)); figure3d.setCP(new Coordinate(Double.NaN, Double.NaN)); } - + if (figure.getType() == RocketPanel.VIEW_TYPE.SideView && length > 0) { extraCP.setPosition(cpx, cpy); extraCG.setPosition(cgx, cgy); @@ -686,34 +689,64 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } // Start calculation process - if(((SwingPreferences) Application.getPreferences()).computeFlightInBackground()){ - extraText.setCalculatingData(true); + if (Application.getPreferences().getAutoRunSimulations()) { + updateSims(false); + } + } - Rocket duplicate = (Rocket) document.getRocket().copy(); + /** + * Updates the simulations. If *currentConfig* is true, only update the simulation of the current flight + * configuration. If it is false, update all the simulations. + * + * @param currentConfig flag to check whether to update all the simulations (false) or only the current + * flight config sim (true) + */ + private void updateSims(boolean currentConfig) { + // Stop previous computation (if any) + stopBackgroundSimulation(); - // find a Simulation based on the current flight configuration - FlightConfigurationId curID = curConfig.getFlightConfigurationID(); - Simulation simulation = null; - for (Simulation sim : document.getSimulations()) { + FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); + extraText.setCalculatingData(true); + Rocket duplicate = (Rocket)document.getRocket().copy(); + + // Re-run the present simulation(s) + List sims = new LinkedList<>(); + for (Simulation sim : document.getSimulations()) { + // Find a Simulation based on the current flight configuration + if (currentConfig) { if (sim.getFlightConfigurationId().compareTo(curID) == 0) { - simulation = sim; + sims.add(sim); break; } } - - // I *think* every FlightConfiguration has at least one associated simulation; just in case I'm wrong, - // if there isn't one we'll create a new simulation to update the statistics in the panel using the - // default simulation conditions - if (simulation == null) { - System.out.println("creating new simulation"); - simulation = ((SwingPreferences) Application.getPreferences()).getBackgroundSimulation(duplicate); - simulation.setFlightConfigurationId( document.getSelectedConfiguration().getId()); - } else - System.out.println("using pre-existing simulation"); - - backgroundSimulationWorker = new BackgroundSimulationWorker(document, simulation); - backgroundSimulationExecutor.execute(backgroundSimulationWorker); + else + sims.add(sim); } + runBackgroundSimulations(sims, duplicate); + } + + /** + * Runs a new background simulation for simulations *sims*. It will run all the simulations in sims sequentially + * in the background. + * + * @param sims simulations which should be run + * @param rkt rocket for which the simulations are run + */ + private void runBackgroundSimulations(List sims, Rocket rkt) { + // I *think* every FlightConfiguration has at least one associated simulation; just in case I'm wrong, + // if there isn't one we'll create a new simulation to update the statistics in the panel using the + // default simulation conditions + for (Simulation sim : sims) { + if (sim == null) { + log.info("creating new simulation"); + sim = ((SwingPreferences) Application.getPreferences()).getBackgroundSimulation(rkt); + sim.setFlightConfigurationId(document.getSelectedConfiguration().getId()); + } else + log.info("using pre-existing simulation"); + } + + backgroundSimulationWorker = new BackgroundSimulationWorker(document, sims); + backgroundSimulationExecutor.execute(backgroundSimulationWorker); } /** @@ -734,16 +767,19 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change private class BackgroundSimulationWorker extends SimulationWorker { private final CustomExpressionSimulationListener exprListener; + private final OpenRocketDocument doc; + private List sims; - public BackgroundSimulationWorker(OpenRocketDocument doc, Simulation sim) { - super(sim); + public BackgroundSimulationWorker(OpenRocketDocument doc, List sims) { + super(sims.get(0)); + this.sims = sims; + this.doc = doc; List exprs = doc.getCustomExpressions(); exprListener = new CustomExpressionSimulationListener(exprs); } @Override protected FlightData doInBackground() { - // Pause a little while to allow faster UI reaction try { Thread.sleep(300); @@ -751,7 +787,6 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change } if (isCancelled() || backgroundSimulationWorker != this) return null; - return super.doInBackground(); } @@ -766,6 +801,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change figure.repaint(); figure3d.repaint(); document.fireDocumentChangeEvent(new SimulationChangeEvent(simulation)); + + // Run the new simulation after this one has ended + this.sims.remove(0); + if (this.sims.size() > 0) { + backgroundSimulationWorker = new BackgroundSimulationWorker(this.doc, this.sims); + backgroundSimulationExecutor.execute(backgroundSimulationWorker); + } } @Override @@ -815,7 +857,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. */ @Override From a57d6a9b5351fe8dc07f2ba876b565ea550aa5f4 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Sun, 18 Jul 2021 03:14:28 +0200 Subject: [PATCH 04/12] [fixes #927] Only update all sims when in simulation tab --- .../sf/openrocket/gui/main/BasicFrame.java | 5 ++- .../gui/scalefigure/RocketPanel.java | 44 +++++++++++-------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 1818a533e..a5a6f27b9 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -221,7 +221,7 @@ public class BasicFrame extends JFrame { // Bottom segment, rocket figure - rocketpanel = new RocketPanel(document); + rocketpanel = new RocketPanel(document, this); vertical.setBottomComponent(rocketpanel); rocketpanel.setSelectionModel(tree.getSelectionModel()); @@ -1099,6 +1099,9 @@ public class BasicFrame extends JFrame { tabbedPane.setSelectedIndex(tab); } + public int getSelectedTab() { + return tabbedPane.getSelectedIndex(); + } private void openAction() { diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 9b1e8f564..fd6f35aa1 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -16,14 +16,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSlider; -import javax.swing.JViewport; -import javax.swing.SwingUtilities; +import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.TreeSelectionEvent; @@ -50,6 +43,7 @@ import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CPCaret; import net.sf.openrocket.gui.figureelements.Caret; import net.sf.openrocket.gui.figureelements.RocketInfo; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.gui.main.componenttree.ComponentTreeModel; import net.sf.openrocket.gui.simulation.SimulationWorker; import net.sf.openrocket.gui.util.SwingPreferences; @@ -63,7 +57,6 @@ import net.sf.openrocket.rocketcomponent.FlightConfigurationId; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.simulation.BasicEventSimulationEngine; import net.sf.openrocket.simulation.FlightData; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener; @@ -153,6 +146,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change private List listeners = new ArrayList(); + // Store the basic frame to know which tab is selected (Rocket design, Motors & Configuration, Flight simulations) + private final BasicFrame basicFrame; + /** * The executor service used for running the background simulations. @@ -178,9 +174,14 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change public OpenRocketDocument getDocument(){ return this.document; } - + public RocketPanel(OpenRocketDocument document) { + this(document, null); + } + + public RocketPanel(OpenRocketDocument document, BasicFrame basicFrame) { this.document = document; + this.basicFrame = basicFrame; Rocket rkt = document.getRocket(); @@ -688,20 +689,25 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change return; } - // Start calculation process + // Update simulations if (Application.getPreferences().getAutoRunSimulations()) { + // Update only current flight config simulation when you are not in the simulations tab + updateSims(this.basicFrame != null && this.basicFrame.getSelectedTab() == BasicFrame.SIMULATION_TAB); + } + else { + // Always update the simulation of the current configuration updateSims(false); } } /** - * Updates the simulations. If *currentConfig* is true, only update the simulation of the current flight - * configuration. If it is false, update all the simulations. + * Updates the simulations. If *currentConfig* is false, only update the simulation of the current flight + * configuration. If it is true, update all the simulations. * - * @param currentConfig flag to check whether to update all the simulations (false) or only the current - * flight config sim (true) + * @param updateAllSims flag to check whether to update all the simulations (true) or only the current + * flight config sim (false) */ - private void updateSims(boolean currentConfig) { + private void updateSims(boolean updateAllSims) { // Stop previous computation (if any) stopBackgroundSimulation(); @@ -713,14 +719,15 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change List sims = new LinkedList<>(); for (Simulation sim : document.getSimulations()) { // Find a Simulation based on the current flight configuration - if (currentConfig) { + if (!updateAllSims) { if (sim.getFlightConfigurationId().compareTo(curID) == 0) { sims.add(sim); break; } } - else + else { sims.add(sim); + } } runBackgroundSimulations(sims, duplicate); } @@ -780,6 +787,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change @Override protected FlightData doInBackground() { + extraText.setCalculatingData(true); // Pause a little while to allow faster UI reaction try { Thread.sleep(300); From 115f679cdc178073e2c59a478f650861b618cc50 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Wed, 11 Aug 2021 02:21:28 +0200 Subject: [PATCH 05/12] [fixes #927] Fix run up-to-date sims on flight configuration change --- .../openrocket/gui/scalefigure/RocketPanel.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index fd6f35aa1..8baab7d3e 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -718,6 +718,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // Re-run the present simulation(s) List sims = new LinkedList<>(); for (Simulation sim : document.getSimulations()) { + if (sim.getStatus() == Simulation.Status.UPTODATE || sim.getStatus() == Simulation.Status.LOADED) + continue; + // Find a Simulation based on the current flight configuration if (!updateAllSims) { if (sim.getFlightConfigurationId().compareTo(curID) == 0) { @@ -740,6 +743,18 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change * @param rkt rocket for which the simulations are run */ private void runBackgroundSimulations(List sims, Rocket rkt) { + if (sims.size() == 0) { + FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); + for (Simulation sim : document.getSimulations()) { + if (sim.getFlightConfigurationId().compareTo(curID) == 0) { + extraText.setFlightData(sim.getSimulatedData()); + break; + } + } + extraText.setCalculatingData(false); + return; + } + // I *think* every FlightConfiguration has at least one associated simulation; just in case I'm wrong, // if there isn't one we'll create a new simulation to update the statistics in the panel using the // default simulation conditions From ee536d3631ca7d363316e29bb0c426befa9b4ae7 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Fri, 13 Aug 2021 11:57:24 +0200 Subject: [PATCH 06/12] [fixes #927] Don't simulate sims with no motors --- swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 8baab7d3e..72a1684af 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -718,7 +718,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // Re-run the present simulation(s) List sims = new LinkedList<>(); for (Simulation sim : document.getSimulations()) { - if (sim.getStatus() == Simulation.Status.UPTODATE || sim.getStatus() == Simulation.Status.LOADED) + if (sim.getStatus() == Simulation.Status.UPTODATE || sim.getStatus() == Simulation.Status.LOADED + || !document.getRocket().getFlightConfiguration(sim.getFlightConfigurationId()).hasMotors()) continue; // Find a Simulation based on the current flight configuration From 3646a8858e04ac057cb5cd1f49c76274ec3f5497 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Tue, 24 Aug 2021 13:48:35 +0200 Subject: [PATCH 07/12] =?UTF-8?q?[fixes=20#927]=C2=A0Only=20update=20fligh?= =?UTF-8?q?t=20data=20info=20for=20current=20flight=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sf/openrocket/gui/scalefigure/RocketPanel.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 72a1684af..02a2eaa7e 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -745,15 +745,15 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change */ private void runBackgroundSimulations(List sims, Rocket rkt) { if (sims.size() == 0) { + extraText.setCalculatingData(false); FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); for (Simulation sim : document.getSimulations()) { if (sim.getFlightConfigurationId().compareTo(curID) == 0) { extraText.setFlightData(sim.getSimulatedData()); - break; + return; } } - extraText.setCalculatingData(false); - return; + extraText.setFlightData(FlightData.NaN_DATA); } // I *think* every FlightConfiguration has at least one associated simulation; just in case I'm wrong, @@ -820,7 +820,12 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change if (isCancelled() || backgroundSimulationWorker != this) return; backgroundSimulationWorker = null; - extraText.setFlightData(simulation.getSimulatedData()); + + // Only set the flight data information of the current flight configuration + FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); + if (simulation.getFlightConfigurationId().compareTo(curID) == 0) { + extraText.setFlightData(simulation.getSimulatedData()); + } extraText.setCalculatingData(false); figure.repaint(); figure3d.repaint(); From 09ac23a54c92dafc61a1c7b584b135dfd982846d Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Thu, 26 Aug 2021 11:41:54 +0200 Subject: [PATCH 08/12] [fixes #927] Update flight data text upon Run simulations action --- .../gui/scalefigure/RocketPanel.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 02a2eaa7e..c585eb6c8 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -58,8 +58,10 @@ import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.simulation.FlightData; +import net.sf.openrocket.simulation.SimulationStatus; import net.sf.openrocket.simulation.customexpression.CustomExpression; import net.sf.openrocket.simulation.customexpression.CustomExpressionSimulationListener; +import net.sf.openrocket.simulation.exception.SimulationException; import net.sf.openrocket.simulation.listeners.SimulationListener; import net.sf.openrocket.simulation.listeners.system.GroundHitListener; import net.sf.openrocket.simulation.listeners.system.InterruptListener; @@ -698,6 +700,21 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // Always update the simulation of the current configuration updateSims(false); } + + // Update flight data and add flight data update trigger upon simulation changes + for (Simulation sim : document.getSimulations()) { + sim.addChangeListener(new StateChangeListener() { + @Override + public void stateChanged(EventObject e) { + if (updateFlightData(sim)) { + updateFigures(); + } + } + }); + if (updateFlightData(sim)) { + break; + } + } } /** @@ -736,6 +753,25 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change runBackgroundSimulations(sims, duplicate); } + /** + * Update the flight data text with the data of {sim}. Only update if sim is the simulation of the current flight + * configuration. + * @param sim: simulation from which the flight data is taken + * @return true if the flight data was updated, false if not + */ + private boolean updateFlightData(Simulation sim) { + FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); + if (sim.getFlightConfigurationId().compareTo(curID) == 0) { + if (sim.hasSimulationData()) { + extraText.setFlightData(sim.getSimulatedData()); + } else { + extraText.setFlightData(FlightData.NaN_DATA); + } + return true; + } + return false; + } + /** * Runs a new background simulation for simulations *sims*. It will run all the simulations in sims sequentially * in the background. @@ -746,14 +782,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change private void runBackgroundSimulations(List sims, Rocket rkt) { if (sims.size() == 0) { extraText.setCalculatingData(false); - FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); for (Simulation sim : document.getSimulations()) { - if (sim.getFlightConfigurationId().compareTo(curID) == 0) { - extraText.setFlightData(sim.getSimulatedData()); + if (updateFlightData(sim)) { return; } } extraText.setFlightData(FlightData.NaN_DATA); + return; } // I *think* every FlightConfiguration has at least one associated simulation; just in case I'm wrong, @@ -822,10 +857,6 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change backgroundSimulationWorker = null; // Only set the flight data information of the current flight configuration - FlightConfigurationId curID = document.getSelectedConfiguration().getFlightConfigurationID(); - if (simulation.getFlightConfigurationId().compareTo(curID) == 0) { - extraText.setFlightData(simulation.getSimulatedData()); - } extraText.setCalculatingData(false); figure.repaint(); figure3d.repaint(); @@ -941,5 +972,5 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change // putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d); // } // } - + } From 894ac897337ef6d1989054c1bda6761707430ed5 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Thu, 26 Aug 2021 11:46:35 +0200 Subject: [PATCH 09/12] [fixes #927] Q&D fix for NullPointerException upon no motors sim run --- .../atmosphere/InterpolatingAtmosphericModel.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java b/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java index 6b383b513..780adc300 100644 --- a/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java +++ b/core/src/net/sf/openrocket/models/atmosphere/InterpolatingAtmosphericModel.java @@ -18,10 +18,20 @@ public abstract class InterpolatingAtmosphericModel implements AtmosphericModel if (levels == null) computeLayers(); - if (altitude <= 0) + if (altitude <= 0) { + // TODO: LOW: levels[0] returned null in some cases, see GitHub issue #952 for more information + if (levels[0] == null) { + computeLayers(); + } return levels[0]; - if (altitude >= DELTA * (levels.length - 1)) + } + if (altitude >= DELTA * (levels.length - 1)) { + // TODO: LOW: levels[levels.length - 1] returned null in some cases, see GitHub issue #952 for more information + if (levels[levels.length - 1] == null) { + computeLayers(); + } return levels[levels.length - 1]; + } int n = (int) (altitude / DELTA); double d = (altitude - n * DELTA) / DELTA; From 9700c9fc86c5854518875b75f6a96e9f865565d0 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Mon, 30 Aug 2021 10:53:41 +0200 Subject: [PATCH 10/12] [fixes #771] Auto update simulations upon motor/recovery/stage configuration change --- .../DeploymentConfiguration.java | 16 +++++++- .../StageSeparationConfiguration.java | 18 ++++++++- .../FlightConfigurablePanel.java | 8 +++- .../FlightConfigurationPanel.java | 19 ++++----- .../MotorConfigurationPanel.java | 39 +++++++++++++------ .../RecoveryConfigurationPanel.java | 17 ++++++-- .../SeparationConfigurationPanel.java | 31 +++++++++------ 7 files changed, 107 insertions(+), 41 deletions(-) diff --git a/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java index 1ab819f11..f30885a60 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/DeploymentConfiguration.java @@ -8,6 +8,8 @@ import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Pair; +import java.util.Objects; + public class DeploymentConfiguration implements FlightConfigurableParameter { @@ -154,5 +156,17 @@ public class DeploymentConfiguration implements FlightConfigurableParameter { public static enum SeparationEvent { @@ -144,8 +146,20 @@ public class StageSeparationConfiguration implements FlightConfigurableParameter clone.separationDelay = this.separationDelay; return clone; } - - + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StageSeparationConfiguration that = (StageSeparationConfiguration) o; + return Double.compare(that.separationDelay, separationDelay) == 0 && separationEvent == that.separationEvent; + } + + @Override + public int hashCode() { + return Objects.hash(separationEvent, separationDelay); + } + private void fireChangeEvent() { } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 39490f44e..54071c1f1 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -54,10 +54,14 @@ public abstract class FlightConfigurablePanel Motor mtr = motorChooserDialog.getSelectedMotor(); double d = motorChooserDialog.getSelectedDelay(); if (mtr != null) { + if (mtr == curMount.getMotorConfig(fcid).getMotor()) { + return; + } final MotorConfiguration templateConfig = curMount.getMotorConfig(fcid); final MotorConfiguration newConfig = new MotorConfiguration( curMount, fcid, templateConfig); newConfig.setMotor(mtr); newConfig.setEjectionDelay(d); curMount.setMotorConfig( newConfig, fcid); - } - fireTableDataChanged(); + fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } } private void removeMotor() { @@ -226,24 +231,30 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel curMount.setMotorConfig( null, fcid); - fireTableDataChanged(); + fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); } private void selectIgnition() { - MotorMount curMount = getSelectedComponent(); - FlightConfigurationId fcid= getSelectedConfigurationId(); - if ( (null == fcid )||( null == curMount )){ - return; - } - + MotorMount curMount = getSelectedComponent(); + FlightConfigurationId fcid = getSelectedConfigurationId(); + if ((null == fcid) || (null == curMount)) { + return; + } + + MotorConfiguration curInstance = curMount.getMotorConfig(fcid); + IgnitionEvent initialIgnitionEvent = curInstance.getIgnitionEvent(); + double initialIgnitionDelay = curInstance.getIgnitionDelay(); + // this call also performs the update changes IgnitionSelectionDialog ignitionDialog = new IgnitionSelectionDialog( SwingUtilities.getWindowAncestor(this.flightConfigurationPanel), fcid, curMount); ignitionDialog.setVisible(true); - - fireTableDataChanged(); + + if (!initialIgnitionEvent.equals(curInstance.getIgnitionEvent()) || (initialIgnitionDelay != curInstance.getIgnitionDelay())) { + fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } } @@ -254,10 +265,14 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel return; } MotorConfiguration curInstance = curMount.getMotorConfig(fcid); + IgnitionEvent initialIgnitionEvent = curInstance.getIgnitionEvent(); + double initialIgnitionDelay = curInstance.getIgnitionDelay(); curInstance.useDefaultIgnition(); - fireTableDataChanged(); + if (!initialIgnitionEvent.equals(curInstance.getIgnitionEvent()) || (initialIgnitionDelay != curInstance.getIgnitionDelay())) { + fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index 80c71a8fd..9fd246ec2 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -99,22 +99,31 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel Date: Mon, 30 Aug 2021 10:59:07 +0200 Subject: [PATCH 11/12] [fixes #771] Fix reset ignition not resetting the values --- core/src/net/sf/openrocket/motor/MotorConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/net/sf/openrocket/motor/MotorConfiguration.java b/core/src/net/sf/openrocket/motor/MotorConfiguration.java index 4920a9421..48a42687c 100644 --- a/core/src/net/sf/openrocket/motor/MotorConfiguration.java +++ b/core/src/net/sf/openrocket/motor/MotorConfiguration.java @@ -115,6 +115,8 @@ public class MotorConfiguration implements FlightConfigurableParameter Date: Mon, 30 Aug 2021 12:20:36 +0200 Subject: [PATCH 12/12] [fixes 771] Fix sims not updating after ejection charge delay change --- .../gui/main/flightconfigpanel/MotorConfigurationPanel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java index 20f81183f..24c235e93 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java @@ -203,13 +203,15 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel throw new IllegalStateException("Attempting to set a motor on the default FCID."); } + double initDelay = curMount.getMotorConfig(fcid).getEjectionDelay(); + motorChooserDialog.setMotorMountAndConfig( fcid, curMount ); motorChooserDialog.setVisible(true); Motor mtr = motorChooserDialog.getSelectedMotor(); double d = motorChooserDialog.getSelectedDelay(); if (mtr != null) { - if (mtr == curMount.getMotorConfig(fcid).getMotor()) { + if (mtr == curMount.getMotorConfig(fcid).getMotor() && d == initDelay) { return; } final MotorConfiguration templateConfig = curMount.getMotorConfig(fcid);