package net.sf.openrocket.optimization.services; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.optimization.general.OptimizationException; import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier; import net.sf.openrocket.optimization.rocketoptimization.modifiers.FlightConfigurationModifier; import net.sf.openrocket.optimization.rocketoptimization.modifiers.GenericComponentModifier; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; import net.sf.openrocket.rocketcomponent.EllipticalFinSet; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FreeformFinSet; import net.sf.openrocket.rocketcomponent.IgnitionConfiguration; import net.sf.openrocket.rocketcomponent.InternalComponent; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.Parachute; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.Streamer; import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.Reflection.Method; public class DefaultSimulationModifierService implements SimulationModifierService { private static final Translator trans = Application.getTranslator(); private static final double DEFAULT_RANGE_MULTIPLIER = 2.0; private static final Map<Class<?>, List<ModifierDefinition>> definitions = new HashMap<Class<?>, List<ModifierDefinition>>(); static { //addModifier("optimization.modifier.", unitGroup, multiplier, componentClass, methodName); /* * Note: Each component type must contain only mutually exclusive variables. * For example, body tube does not have inner diameter definition because it is * defined by the outer diameter and thickness. */ addModifier("optimization.modifier.nosecone.length", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Length"); addModifier("optimization.modifier.nosecone.diameter", UnitGroup.UNITS_LENGTH, 2.0, NoseCone.class, "AftRadius", "isAftRadiusAutomatic"); addModifier("optimization.modifier.nosecone.thickness", UnitGroup.UNITS_LENGTH, 1.0, NoseCone.class, "Thickness", "isFilled"); addModifier("optimization.modifier.transition.length", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Length"); addModifier("optimization.modifier.transition.forediameter", UnitGroup.UNITS_LENGTH, 2.0, Transition.class, "ForeRadius", "isForeRadiusAutomatic"); addModifier("optimization.modifier.transition.aftdiameter", UnitGroup.UNITS_LENGTH, 2.0, Transition.class, "AftRadius", "isAftRadiusAutomatic"); addModifier("optimization.modifier.transition.thickness", UnitGroup.UNITS_LENGTH, 1.0, Transition.class, "Thickness", "isFilled"); addModifier("optimization.modifier.bodytube.length", UnitGroup.UNITS_LENGTH, 1.0, BodyTube.class, "Length"); addModifier("optimization.modifier.bodytube.outerDiameter", UnitGroup.UNITS_LENGTH, 2.0, BodyTube.class, "OuterRadius", "isOuterRadiusAutomatic"); addModifier("optimization.modifier.bodytube.thickness", UnitGroup.UNITS_LENGTH, 1.0, BodyTube.class, "Thickness", "isFilled"); addModifier("optimization.modifier.trapezoidfinset.rootChord", UnitGroup.UNITS_LENGTH, 1.0, TrapezoidFinSet.class, "RootChord"); addModifier("optimization.modifier.trapezoidfinset.tipChord", UnitGroup.UNITS_LENGTH, 1.0, TrapezoidFinSet.class, "TipChord"); addModifier("optimization.modifier.trapezoidfinset.sweep", UnitGroup.UNITS_LENGTH, 1.0, TrapezoidFinSet.class, "Sweep"); addModifier("optimization.modifier.trapezoidfinset.height", UnitGroup.UNITS_LENGTH, 1.0, TrapezoidFinSet.class, "Height"); addModifier("optimization.modifier.finset.cant", UnitGroup.UNITS_ANGLE, 1.0, TrapezoidFinSet.class, "CantAngle"); addModifier("optimization.modifier.ellipticalfinset.length", UnitGroup.UNITS_LENGTH, 1.0, EllipticalFinSet.class, "Length"); addModifier("optimization.modifier.ellipticalfinset.height", UnitGroup.UNITS_LENGTH, 1.0, EllipticalFinSet.class, "Height"); addModifier("optimization.modifier.finset.cant", UnitGroup.UNITS_ANGLE, 1.0, EllipticalFinSet.class, "CantAngle"); addModifier("optimization.modifier.finset.cant", UnitGroup.UNITS_ANGLE, 1.0, FreeformFinSet.class, "CantAngle"); addModifier("optimization.modifier.launchlug.length", UnitGroup.UNITS_LENGTH, 1.0, LaunchLug.class, "Length"); addModifier("optimization.modifier.launchlug.outerDiameter", UnitGroup.UNITS_LENGTH, 2.0, LaunchLug.class, "OuterRadius"); addModifier("optimization.modifier.launchlug.thickness", UnitGroup.UNITS_LENGTH, 1.0, LaunchLug.class, "Thickness"); addModifier("optimization.modifier.masscomponent.mass", UnitGroup.UNITS_MASS, 1.0, MassComponent.class, "ComponentMass"); addModifier("optimization.modifier.parachute.diameter", UnitGroup.UNITS_LENGTH, 1.0, Parachute.class, "Diameter"); addModifier("optimization.modifier.parachute.coefficient", UnitGroup.UNITS_NONE, 1.0, Parachute.class, "CD"); addModifier("optimization.modifier.streamer.length", UnitGroup.UNITS_LENGTH, 1.0, Streamer.class, "StripLength"); addModifier("optimization.modifier.streamer.width", UnitGroup.UNITS_LENGTH, 1.0, Streamer.class, "StripWidth"); addModifier("optimization.modifier.streamer.aspectRatio", UnitGroup.UNITS_NONE, 1.0, Streamer.class, "AspectRatio"); addModifier("optimization.modifier.streamer.coefficient", UnitGroup.UNITS_NONE, 1.0, Streamer.class, "CD", "isCDAutomatic"); } private static void addModifier(String modifierNameKey, UnitGroup unitGroup, double multiplier, Class<? extends RocketComponent> componentClass, String methodName) { addModifier(modifierNameKey, unitGroup, multiplier, componentClass, methodName, null); } private static void addModifier(String modifierNameKey, UnitGroup unitGroup, double multiplier, Class<? extends RocketComponent> componentClass, String methodName, String autoMethod) { String modifierDescriptionKey = modifierNameKey + ".desc"; List<ModifierDefinition> list = definitions.get(componentClass); if (list == null) { list = new ArrayList<DefaultSimulationModifierService.ModifierDefinition>(); definitions.put(componentClass, list); } ModifierDefinition definition = new ModifierDefinition(modifierNameKey, modifierDescriptionKey, unitGroup, multiplier, componentClass, methodName, autoMethod); list.add(definition); } @Override public Collection<SimulationModifier> getModifiers(OpenRocketDocument document) { List<SimulationModifier> modifiers = new ArrayList<SimulationModifier>(); Rocket rocket = document.getRocket(); // Simulation is used to calculate default min/max values Simulation simulation = new Simulation(rocket); simulation.getConfiguration().setFlightConfigurationID(null); for (RocketComponent c : rocket) { // Attribute modifiers List<ModifierDefinition> list = definitions.get(c.getClass()); if (list != null) { for (ModifierDefinition def : list) { // Ignore modifier if value is set to automatic if (def.autoMethod != null) { Method m = Reflection.findMethod(c.getClass(), def.autoMethod); if ((Boolean) m.invoke(c)) { continue; } } SimulationModifier mod = new GenericComponentModifier( trans.get(def.modifierNameKey), trans.get(def.modifierDescriptionKey), c, def.unitGroup, def.multiplier, def.componentClass, c.getID(), def.methodName); setDefaultMinMax(mod, simulation); modifiers.add(mod); } } // Add override modifiers if mass/CG is overridden if (c.isMassOverridden()) { SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.rocketcomponent.overrideMass"), trans.get("optimization.modifier.rocketcomponent.overrideMass.desc"), c, UnitGroup.UNITS_MASS, 1.0, c.getClass(), c.getID(), "OverrideMass"); setDefaultMinMax(mod, simulation); modifiers.add(mod); } if (c.isCGOverridden()) { SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.rocketcomponent.overrideCG"), trans.get("optimization.modifier.rocketcomponent.overrideCG.desc"), c, UnitGroup.UNITS_LENGTH, 1.0, c.getClass(), c.getID(), "OverrideCGX"); mod.setMinValue(0); mod.setMaxValue(c.getLength()); modifiers.add(mod); } // Conditional motor mount parameters if (c instanceof MotorMount) { MotorMount mount = (MotorMount) c; if (mount.isMotorMount()) { SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.motormount.overhang"), trans.get("optimization.modifier.motormount.overhang.desc"), c, UnitGroup.UNITS_LENGTH, 1.0, c.getClass(), c.getID(), "MotorOverhang"); setDefaultMinMax(mod, simulation); modifiers.add(mod); mod = new FlightConfigurationModifier<IgnitionConfiguration>( trans.get("optimization.modifier.motormount.delay"), trans.get("optimization.modifier.motormount.delay.desc"), c, UnitGroup.UNITS_SHORT_TIME, 1.0, c.getClass(), c.getID(), "IgnitionConfiguration", IgnitionConfiguration.class, "IgnitionDelay"); mod.setMinValue(0); mod.setMaxValue(5); modifiers.add(mod); } } // Inner component positioning if (c instanceof InternalComponent) { RocketComponent parent = c.getParent(); SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.internalcomponent.position"), trans.get("optimization.modifier.internalcomponent.position.desc"), c, UnitGroup.UNITS_LENGTH, 1.0, c.getClass(), c.getID(), "PositionValue"); mod.setMinValue(0); mod.setMaxValue(parent.getLength()); modifiers.add(mod); } // Custom min/max for fin set position if (c instanceof FinSet) { RocketComponent parent = c.getParent(); SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.finset.position"), trans.get("optimization.modifier.finset.position.desc"), c, UnitGroup.UNITS_LENGTH, 1.0, c.getClass(), c.getID(), "PositionValue"); mod.setMinValue(0); mod.setMaxValue(parent.getLength()); modifiers.add(mod); } // Custom min/max for launch lug position if (c instanceof LaunchLug) { RocketComponent parent = c.getParent(); SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier.launchlug.position"), trans.get("optimization.modifier.launchlug.position.desc"), c, UnitGroup.UNITS_LENGTH, 1.0, c.getClass(), c.getID(), "PositionValue"); mod.setMinValue(0); mod.setMaxValue(parent.getLength()); modifiers.add(mod); } // Recovery device deployment altitude and delay if (c instanceof RecoveryDevice) { RecoveryDevice device = (RecoveryDevice) c; SimulationModifier mod = new FlightConfigurationModifier<DeploymentConfiguration>( trans.get("optimization.modifier.recoverydevice.deployDelay"), trans.get("optimization.modifier.recoverydevice.deployDelay.desc"), c, UnitGroup.UNITS_SHORT_TIME, 1.0, c.getClass(), c.getID(), "DeploymentConfiguration", DeploymentConfiguration.class, "DeployDelay"); mod.setMinValue(0); mod.setMaxValue(10); modifiers.add(mod); mod = new FlightConfigurationModifier<DeploymentConfiguration>( trans.get("optimization.modifier.recoverydevice.deployAltitude"), trans.get("optimization.modifier.recoverydevice.deployAltitude.desc"), c, UnitGroup.UNITS_DISTANCE, 1.0, c.getClass(), c.getID(), "DeploymentConfiguration", DeploymentConfiguration.class, "DeployAltitude") { @Override public void initialize(Simulation simulation) throws OptimizationException { DeploymentConfiguration config = getModifiedObject(simulation); config.setDeployEvent(DeployEvent.APOGEE); } }; setDefaultMinMax(mod, simulation); modifiers.add(mod); } // Conditional shape parameter of Transition if (c instanceof Transition) { Transition transition = (Transition) c; Transition.Shape shape = transition.getType(); if (shape.usesParameter()) { SimulationModifier mod = new GenericComponentModifier( trans.get("optimization.modifier." + c.getClass().getSimpleName().toLowerCase(Locale.ENGLISH) + ".shapeparameter"), trans.get("optimization.modifier." + c.getClass().getSimpleName().toLowerCase(Locale.ENGLISH) + ".shapeparameter.desc"), c, UnitGroup.UNITS_NONE, 1.0, c.getClass(), c.getID(), "ShapeParameter"); mod.setMinValue(shape.minParameter()); mod.setMaxValue(shape.maxParameter()); modifiers.add(mod); } } } return modifiers; } private void setDefaultMinMax(SimulationModifier mod, Simulation simulation) { try { double current = mod.getCurrentSIValue(simulation); mod.setMinValue(current / DEFAULT_RANGE_MULTIPLIER); mod.setMaxValue(current * DEFAULT_RANGE_MULTIPLIER); } catch (OptimizationException e) { throw new BugException("Simulation modifier threw exception", e); } } /* * String modifierName, Object relatedObject, UnitGroup unitGroup, double multiplier, Class<? extends RocketComponent> componentClass, String componentId, String methodName */ private static class ModifierDefinition { private final String modifierNameKey; private final String modifierDescriptionKey; private final UnitGroup unitGroup; private final double multiplier; private final Class<? extends RocketComponent> componentClass; private final String methodName; private final String autoMethod; public ModifierDefinition(String modifierNameKey, String modifierDescriptionKey, UnitGroup unitGroup, double multiplier, Class<? extends RocketComponent> componentClass, String methodName, String autoMethod) { this.modifierNameKey = modifierNameKey; this.modifierDescriptionKey = modifierDescriptionKey; this.unitGroup = unitGroup; this.multiplier = multiplier; this.componentClass = componentClass; this.methodName = methodName; this.autoMethod = autoMethod; } } }